1 # -*- coding: utf-8 -*-
5 from constants import TESTBED_ID, TESTBED_VERSION
6 from nepi.core import metadata
7 from nepi.core.metadata import Parallel
8 from nepi.core.attributes import Attribute
9 from nepi.util import tags, validation
10 from nepi.util.constants import ApplicationStatus as AS, \
11 FactoryCategories as FC, \
12 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP, \
13 DeploymentConfiguration as DC
21 NODEIFACE = "NodeInterface"
22 TUNIFACE = "TunInterface"
23 TAPIFACE = "TapInterface"
24 APPLICATION = "Application"
25 CCNXDAEMON = "CCNxDaemon"
26 DEPENDENCY = "Dependency"
27 NEPIDEPENDENCY = "NepiDependency"
28 NS3DEPENDENCY = "NS3Dependency"
31 TUNFILTER = "TunFilter"
32 CLASSQUEUEFILTER = "ClassQueueFilter"
33 LOGGINGCLASSQUEUEFILTER = "LoggingClassQueueFilter"
34 TOSQUEUEFILTER = "TosQueueFilter"
35 MULTICASTFORWARDER = "MulticastForwarder"
36 MULTICASTANNOUNCER = "MulticastAnnouncer"
37 MULTICASTROUTER = "MulticastRouter"
39 TUNFILTERS = (TUNFILTER, CLASSQUEUEFILTER, LOGGINGCLASSQUEUEFILTER, TOSQUEUEFILTER)
40 TAPFILTERS = (TUNFILTER, )
41 ALLFILTERS = (TUNFILTER, CLASSQUEUEFILTER, LOGGINGCLASSQUEUEFILTER, TOSQUEUEFILTER)
43 PL_TESTBED_ID = "planetlab"
46 ### Custom validation functions ###
47 def is_addrlist(attribute, value):
48 if not validation.is_string(attribute, value):
55 components = value.split(',')
57 for component in components:
59 addr, mask = component.split('/',1)
61 addr, mask = component, '32'
63 if mask is not None and not (mask and mask.isdigit()):
64 # No empty or nonnumeric masks
67 if not validation.is_ip4_address(attribute, addr):
68 # Address part must be ipv4
73 def is_portlist(attribute, value):
74 if not validation.is_string(attribute, value):
81 components = value.split(',')
83 for component in components:
85 pfrom, pto = component.split('-',1)
87 pfrom = pto = component
89 if not pfrom or not pto or not pfrom.isdigit() or not pto.isdigit():
90 # No empty or nonnumeric ports
96 ### Connection functions ####
98 def connect_node_iface_node(testbed_instance, node_guid, iface_guid):
99 node = testbed_instance._elements[node_guid]
100 iface = testbed_instance._elements[iface_guid]
103 def connect_node_iface_inet(testbed_instance, iface_guid, inet_guid):
104 iface = testbed_instance._elements[iface_guid]
105 iface.has_internet = True
107 def connect_tun_iface_node(testbed_instance, node_guid, iface_guid):
108 node = testbed_instance._elements[node_guid]
109 iface = testbed_instance._elements[iface_guid]
111 node.required_vsys.update(('fd_tuntap', 'vif_up', 'vif_down'))
112 node.required_packages.update(('python', 'python-crypto', 'python-setuptools', 'gcc'))
114 def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid):
115 iface = testbed_instance._elements[iface_guid]
116 peer_iface = testbed_instance._elements[peer_iface_guid]
117 iface.peer_iface = peer_iface
118 peer_iface.peer_iface = iface
121 peer_iface.peer_proto = \
122 peer_iface.tun_proto = proto
123 iface.tun_key = peer_iface.tun_key
125 def connect_tun_iface_filter(testbed_instance, iface_guid, filter_guid):
126 iface = testbed_instance._elements[iface_guid]
127 filt = testbed_instance._elements[filter_guid]
128 traces = testbed_instance._get_traces(filter_guid)
129 if 'dropped_stats' in traces:
130 args = filt.args if filt.args else ""
131 filt.args = ','.join(filt.args.split(',') + ["logdropped=true",])
132 iface.filter_module = filt
133 filt.iface_guid = iface_guid
134 filt.iface = weakref.ref(iface)
137 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
139 def connect_filter_peer(proto, testbed_instance, filter_guid, peer_guid):
140 peer = testbed_instance._elements[peer_guid]
141 filt = testbed_instance._elements[filter_guid]
142 filt.peer_proto = proto
143 filt.peer_guid = peer_guid
145 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
147 def connect_filter_filter(proto, testbed_instance, filter_guid, peer_guid):
148 peer = testbed_instance._elements[peer_guid]
149 filt = testbed_instance._elements[filter_guid]
150 filt.peer_proto = proto
151 peer.peer_proto = proto
153 peer.peer_guid = filt.iface_guid
155 filt.peer_guid = peer.iface_guid
156 if filt.iface_guid and filt.peer_guid:
157 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
159 def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data):
160 iface = testbed_instance._elements[iface_guid]
161 iface.peer_iface = None
162 iface.peer_addr = peer_iface_data.get("tun_addr")
163 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
164 iface.peer_port = peer_iface_data.get("tun_port")
165 iface.peer_cipher = peer_iface_data.get("tun_cipher")
166 iface.tun_key = min(iface.tun_key, peer_iface_data.get("tun_key"))
167 iface.tun_proto = proto
169 preconfigure_tuniface(testbed_instance, iface_guid)
171 def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data):
172 # refresh (refreshable) attributes for second-phase
173 iface = testbed_instance._elements[iface_guid]
174 iface.peer_addr = peer_iface_data.get("tun_addr")
175 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
176 iface.peer_port = peer_iface_data.get("tun_port")
177 iface.peer_cipher = peer_iface_data.get("tun_cipher")
179 postconfigure_tuniface(testbed_instance, iface_guid)
181 def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_iface_data):
182 crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
183 crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
185 def crossconnect_filter_peer_init(proto, testbed_instance, filter_guid, peer_data):
186 filt = testbed_instance._elements[filter_guid]
187 filt.peer_proto = proto
188 crossconnect_tun_iface_peer_init(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
190 def crossconnect_filter_peer_compl(proto, testbed_instance, filter_guid, peer_data):
191 filt = testbed_instance._elements[filter_guid]
192 filt.peer_proto = proto
193 crossconnect_tun_iface_peer_compl(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
195 def crossconnect_filter_peer_both(proto, testbed_instance, filter_guid, peer_data):
196 crossconnect_filter_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
197 crossconnect_filter_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
199 def connect_dep(testbed_instance, node_guid, app_guid, node=None, app=None):
200 node = node or testbed_instance._elements[node_guid]
201 app = app or testbed_instance._elements[app_guid]
205 node.required_packages.update(set(
206 app.depends.split() ))
209 if app.home_path and app.home_path not in node.pythonpath:
210 node.pythonpath.append(app.home_path)
213 for envkey, envval in app.env.iteritems():
214 envval = app._replace_paths(envval)
215 node.env[envkey].append(envval)
218 node.rpmFusion = True
220 def connect_forwarder(testbed_instance, node_guid, fwd_guid):
221 node = testbed_instance._elements[node_guid]
222 fwd = testbed_instance._elements[fwd_guid]
223 node.multicast_forwarder = fwd
226 connect_dep(testbed_instance, node_guid, None, app=fwd.router)
228 connect_dep(testbed_instance, node_guid, fwd_guid)
230 def connect_router(testbed_instance, fwd_guid, router_guid):
231 fwd = testbed_instance._elements[fwd_guid]
232 router = testbed_instance._elements[router_guid]
236 connect_dep(testbed_instance, None, router_guid, node=fwd.node)
238 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
239 node = testbed_instance._elements[node_guid]
240 netpipe = testbed_instance._elements[netpipe_guid]
242 node.required_vsys.add('ipfw-be')
243 node.required_packages.add('ipfwslice')
246 ### Creation functions ###
248 def create_node(testbed_instance, guid):
249 parameters = testbed_instance._get_parameters(guid)
251 # create element with basic attributes
252 element = testbed_instance._make_node(parameters)
254 # add constraint on number of (real) interfaces
255 # by counting connected devices
256 dev_guids = testbed_instance.get_connected(guid, "devs", "node")
257 num_open_ifaces = sum( # count True values
258 NODEIFACE == testbed_instance._get_factory_id(guid)
259 for guid in dev_guids )
260 element.min_num_external_ifaces = num_open_ifaces
262 # require vroute vsys if we have routes to set up
263 routes = testbed_instance._add_route.get(guid)
265 vsys = element.routing_method(routes,
266 testbed_instance.vsys_vnet)
267 element.required_vsys.add(vsys)
269 testbed_instance.elements[guid] = element
271 def create_nodeiface(testbed_instance, guid):
272 parameters = testbed_instance._get_parameters(guid)
273 element = testbed_instance._make_node_iface(parameters)
274 testbed_instance.elements[guid] = element
276 def create_tuniface(testbed_instance, guid):
277 parameters = testbed_instance._get_parameters(guid)
278 element = testbed_instance._make_tun_iface(parameters)
280 # Set custom addresses, if there are any already
281 # Setting this early helps set up P2P links
282 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
283 addresses = testbed_instance._add_address[guid]
284 for address in addresses:
285 (address, netprefix, broadcast) = address
286 element.add_address(address, netprefix, broadcast)
288 testbed_instance.elements[guid] = element
290 def create_tapiface(testbed_instance, guid):
291 parameters = testbed_instance._get_parameters(guid)
292 element = testbed_instance._make_tap_iface(parameters)
294 # Set custom addresses, if there are any already
295 # Setting this early helps set up P2P links
296 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
297 addresses = testbed_instance._add_address[guid]
298 for address in addresses:
299 (address, netprefix, broadcast) = address
300 element.add_address(address, netprefix, broadcast)
302 testbed_instance.elements[guid] = element
304 def create_tunfilter(testbed_instance, guid):
305 parameters = testbed_instance._get_parameters(guid)
306 element = testbed_instance._make_tun_filter(parameters)
307 testbed_instance.elements[guid] = element
309 def create_classqueuefilter(testbed_instance, guid):
310 parameters = testbed_instance._get_parameters(guid)
311 element = testbed_instance._make_class_queue_filter(parameters)
312 testbed_instance.elements[guid] = element
314 def create_loggingclassqueuefilter(testbed_instance, guid):
315 parameters = testbed_instance._get_parameters(guid)
316 element = testbed_instance._make_logging_class_queue_filter(parameters)
317 testbed_instance.elements[guid] = element
319 def create_tosqueuefilter(testbed_instance, guid):
320 parameters = testbed_instance._get_parameters(guid)
321 element = testbed_instance._make_tos_queue_filter(parameters)
322 testbed_instance.elements[guid] = element
324 def create_application(testbed_instance, guid):
325 parameters = testbed_instance._get_parameters(guid)
326 element = testbed_instance._make_application(parameters)
328 # Just inject configuration stuff
329 element.home_path = "nepi-app-%s" % (guid,)
331 testbed_instance.elements[guid] = element
333 def create_ccnxdaemon(testbed_instance, guid):
334 parameters = testbed_instance._get_parameters(guid)
335 element = testbed_instance._make_application(parameters,
336 clazz = testbed_instance._app.CCNxDaemon )
338 # Just inject configuration stuff
339 element.home_path = "nepi-ccnd-%s" % (guid,)
341 testbed_instance.elements[guid] = element
343 def create_dependency(testbed_instance, guid):
344 parameters = testbed_instance._get_parameters(guid)
345 element = testbed_instance._make_dependency(parameters)
347 # Just inject configuration stuff
348 element.home_path = "nepi-dep-%s" % (guid,)
350 testbed_instance.elements[guid] = element
352 def create_nepi_dependency(testbed_instance, guid):
353 parameters = testbed_instance._get_parameters(guid)
354 element = testbed_instance._make_nepi_dependency(parameters)
356 # Just inject configuration stuff
357 element.home_path = "nepi-nepi-%s" % (guid,)
359 testbed_instance.elements[guid] = element
361 def create_ns3_dependency(testbed_instance, guid):
362 parameters = testbed_instance._get_parameters(guid)
363 element = testbed_instance._make_ns3_dependency(parameters)
365 # Just inject configuration stuff
366 element.home_path = "nepi-ns3-%s" % (guid,)
368 testbed_instance.elements[guid] = element
370 def create_multicast_forwarder(testbed_instance, guid):
371 parameters = testbed_instance._get_parameters(guid)
372 element = testbed_instance._make_multicast_forwarder(parameters)
374 # Just inject configuration stuff
375 element.home_path = "nepi-mcfwd-%s" % (guid,)
377 testbed_instance.elements[guid] = element
379 def create_multicast_announcer(testbed_instance, guid):
380 parameters = testbed_instance._get_parameters(guid)
381 element = testbed_instance._make_multicast_announcer(parameters)
383 # Just inject configuration stuff
384 element.home_path = "nepi-mcann-%s" % (guid,)
386 testbed_instance.elements[guid] = element
388 def create_multicast_router(testbed_instance, guid):
389 parameters = testbed_instance._get_parameters(guid)
390 element = testbed_instance._make_multicast_router(parameters)
392 # Just inject configuration stuff
393 element.home_path = "nepi-mcrt-%s" % (guid,)
395 testbed_instance.elements[guid] = element
397 def create_internet(testbed_instance, guid):
398 parameters = testbed_instance._get_parameters(guid)
399 element = testbed_instance._make_internet(parameters)
400 testbed_instance.elements[guid] = element
402 def create_netpipe(testbed_instance, guid):
403 parameters = testbed_instance._get_parameters(guid)
404 element = testbed_instance._make_netpipe(parameters)
405 testbed_instance.elements[guid] = element
407 ### Start/Stop functions ###
409 def prestart_ccnxdaemon(testbed_instance, guid):
410 # ccnx daemon needs to start before the rest of the
412 start_application(testbed_instance, guid)
414 def stop_ccndaemon(testbed_instance, guid):
415 app = testbed_instance.elements[guid]
418 def start_application(testbed_instance, guid):
419 parameters = testbed_instance._get_parameters(guid)
420 traces = testbed_instance._get_traces(guid)
421 app = testbed_instance.elements[guid]
423 app.stdout = "stdout" in traces
424 app.stderr = "stderr" in traces
425 app.buildlog = "buildlog" in traces
426 app.outout = "output" in traces
430 def stop_application(testbed_instance, guid):
431 app = testbed_instance.elements[guid]
434 ### Status functions ###
436 def status_application(testbed_instance, guid):
437 if guid not in testbed_instance.elements.keys():
438 return AS.STATUS_NOT_STARTED
440 app = testbed_instance.elements[guid]
443 def status_dependency(testbed_instance, guid):
444 if guid not in testbed_instance.elements.keys():
445 return AS.STATUS_NOT_STARTED
447 dep = testbed_instance.elements[guid]
449 return AS.STATUS_FINISHED
450 return AS.STATUS_RUNNING
452 ### Configure functions ###
454 def configure_nodeiface(testbed_instance, guid):
455 element = testbed_instance._elements[guid]
457 # Cannot explicitly configure addresses
458 if guid in testbed_instance._add_address:
459 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
462 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
463 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
464 siblings = [ self._element[dev_guid]
465 for dev_guid in dev_guids
466 if dev_guid != guid ]
468 # Fetch address from PLC api
469 element.pick_iface(siblings)
471 # Do some validations
474 def preconfigure_tuniface(testbed_instance, guid):
475 element = testbed_instance._elements[guid]
477 # Set custom addresses if any, and if not set already
478 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
479 addresses = testbed_instance._add_address[guid]
480 for address in addresses:
481 (address, netprefix, broadcast) = address
482 element.add_address(address, netprefix, broadcast)
484 # Link to external interface, if any
485 for iface in testbed_instance._elements.itervalues():
486 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
487 element.external_iface = iface
490 # Set standard TUN attributes
491 if (not element.tun_addr or not element.tun_port) and element.external_iface:
492 element.tun_addr = element.external_iface.address
493 element.tun_port = testbed_instance.tapPortBase + int(guid)
496 traces = testbed_instance._get_traces(guid)
497 for capmode in ('pcap', 'packets'):
498 if capmode in traces:
499 element.capture = capmode
502 element.capture = False
504 # Do some validations
508 element.prepare('tun-%s' % (guid,))
510 def postconfigure_tuniface(testbed_instance, guid):
511 element = testbed_instance._elements[guid]
516 def prestart_tuniface(testbed_instance, guid):
517 element = testbed_instance._elements[guid]
522 def configure_node(testbed_instance, guid):
523 node = testbed_instance._elements[guid]
525 # Just inject configuration stuff
526 node.home_path = "nepi-node-%s" % (guid,)
527 node.ident_path = testbed_instance.sliceSSHKey
528 node.slicename = testbed_instance.slicename
530 # Do some validations
533 # this will be done in parallel in all nodes
534 # this call only spawns the process
535 node.install_dependencies()
537 def configure_node_routes(testbed_instance, guid):
538 node = testbed_instance._elements[guid]
539 routes = testbed_instance._add_route.get(guid)
543 for dev_guid in testbed_instance.get_connected(guid, "devs", "node")
544 for dev in ( testbed_instance._elements.get(dev_guid) ,)
545 if dev and isinstance(dev, testbed_instance._interfaces.TunIface) ]
547 vsys = testbed_instance.vsys_vnet
549 node.configure_routes(routes, devs, vsys)
551 def configure_application(testbed_instance, guid):
552 app = testbed_instance._elements[guid]
554 # Do some validations
557 # Wait for dependencies
558 app.node.wait_dependencies()
563 def configure_dependency(testbed_instance, guid):
564 dep = testbed_instance._elements[guid]
566 # Do some validations
569 # Wait for dependencies
570 dep.node.wait_dependencies()
575 def configure_announcer(testbed_instance, guid):
577 fwd = testbed_instance._elements[guid]
579 for node_guid in testbed_instance.get_connected(guid, "node", "apps")
580 for dev_guid in testbed_instance.get_connected(node_guid, "devs", "node")
581 for dev in ( testbed_instance._elements.get(dev_guid) ,)
582 if dev and isinstance(dev, testbed_instance._interfaces.TunIface)
586 configure_dependency(testbed_instance, guid)
588 def configure_forwarder(testbed_instance, guid):
589 configure_announcer(testbed_instance, guid)
591 # Link ifaces to forwarder
592 fwd = testbed_instance._elements[guid]
593 for iface in fwd.ifaces:
594 iface.multicast_forwarder = '/var/run/mcastfwd'
596 def configure_router(testbed_instance, guid):
598 rt = testbed_instance._elements[guid]
600 for fwd_guid in testbed_instance.get_connected(guid, "fwd", "router")
601 for node_guid in testbed_instance.get_connected(fwd_guid, "node", "apps")
602 for dev_guid in testbed_instance.get_connected(node_guid, "devs", "node")
603 for dev in ( testbed_instance._elements.get(dev_guid) ,)
604 if dev and isinstance(dev, testbed_instance._interfaces.TunIface)
605 and not dev.multicast ]
608 configure_dependency(testbed_instance, guid)
610 def configure_netpipe(testbed_instance, guid):
611 netpipe = testbed_instance._elements[guid]
613 # Do some validations
616 # Wait for dependencies
617 netpipe.node.wait_dependencies()
622 ### Factory information ###
624 connector_types = dict({
626 "help": "Connector from node to applications",
632 "help": "Connector from node to network interfaces",
638 "help": "Connector from node to application dependencies "
639 "(packages and applications that need to be installed)",
645 "help": "Connector from network interfaces to the internet",
651 "help": "Connector to a Node",
657 "help": "Connector to a routing daemon",
663 "help": "Forwarder this routing daemon communicates with",
669 "help": "Connector to a NetPipe",
676 "help": "ip-ip tunneling over TCP link",
682 "help": "ip-ip tunneling over UDP datagrams",
688 "help": "IP or Ethernet tunneling using the GRE protocol",
694 "help": "TUN device file descriptor provider",
700 "help": "TUN device file descriptor slot",
709 "from": (TESTBED_ID, NODE, "devs"),
710 "to": (TESTBED_ID, NODEIFACE, "node"),
711 "init_code": connect_node_iface_node,
715 "from": (TESTBED_ID, NODE, "devs"),
716 "to": (TESTBED_ID, TUNIFACE, "node"),
717 "init_code": connect_tun_iface_node,
721 "from": (TESTBED_ID, NODE, "devs"),
722 "to": (TESTBED_ID, TAPIFACE, "node"),
723 "init_code": connect_tun_iface_node,
727 "from": (TESTBED_ID, NODEIFACE, "inet"),
728 "to": (TESTBED_ID, INTERNET, "devs"),
729 "init_code": connect_node_iface_inet,
733 "from": (TESTBED_ID, NODE, "apps"),
734 "to": (TESTBED_ID, (APPLICATION, CCNXDAEMON, MULTICASTANNOUNCER), "node"),
735 "init_code": connect_dep,
739 "from": (TESTBED_ID, NODE, "deps"),
740 "to": (TESTBED_ID, (DEPENDENCY, NEPIDEPENDENCY, NS3DEPENDENCY), "node"),
741 "init_code": connect_dep,
745 "from": (TESTBED_ID, NODE, "pipes"),
746 "to": (TESTBED_ID, NETPIPE, "node"),
747 "init_code": connect_node_netpipe,
751 "from": (TESTBED_ID, NODE, "apps"),
752 "to": (TESTBED_ID, MULTICASTFORWARDER, "node"),
753 "init_code": connect_forwarder,
757 "from": (TESTBED_ID, MULTICASTFORWARDER, "router"),
758 "to": (TESTBED_ID, MULTICASTROUTER, "fwd"),
759 "init_code": connect_router,
763 "from": (TESTBED_ID, TUNIFACE, "tcp"),
764 "to": (TESTBED_ID, TUNIFACE, "tcp"),
765 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
769 "from": (TESTBED_ID, TUNIFACE, "udp"),
770 "to": (TESTBED_ID, TUNIFACE, "udp"),
771 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
775 "from": (TESTBED_ID, TUNIFACE, "gre"),
776 "to": (TESTBED_ID, TUNIFACE, "gre"),
777 "init_code": functools.partial(connect_tun_iface_peer,"gre"),
781 "from": (TESTBED_ID, TUNIFACE, "fd->"),
782 "to": (TESTBED_ID, TUNFILTERS, "->fd"),
783 "init_code": connect_tun_iface_filter,
787 "from": (TESTBED_ID, TUNFILTERS, "tcp"),
788 "to": (TESTBED_ID, TUNIFACE, "tcp"),
789 "init_code": functools.partial(connect_filter_peer,"tcp"),
793 "from": (TESTBED_ID, TUNFILTERS, "udp"),
794 "to": (TESTBED_ID, TUNIFACE, "udp"),
795 "init_code": functools.partial(connect_filter_peer,"udp"),
799 "from": (TESTBED_ID, TAPIFACE, "tcp"),
800 "to": (TESTBED_ID, TAPIFACE, "tcp"),
801 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
805 "from": (TESTBED_ID, TAPIFACE, "udp"),
806 "to": (TESTBED_ID, TAPIFACE, "udp"),
807 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
811 "from": (TESTBED_ID, TAPIFACE, "gre"),
812 "to": (TESTBED_ID, TAPIFACE, "gre"),
813 "init_code": functools.partial(connect_tun_iface_peer,"gre"),
817 "from": (TESTBED_ID, TAPIFACE, "fd->"),
818 "to": (TESTBED_ID, TAPFILTERS, "->fd"),
819 "init_code": connect_tun_iface_filter,
823 "from": (TESTBED_ID, TAPFILTERS, "tcp"),
824 "to": (TESTBED_ID, TAPIFACE, "tcp"),
825 "init_code": functools.partial(connect_filter_peer,"tcp"),
829 "from": (TESTBED_ID, TAPFILTERS, "udp"),
830 "to": (TESTBED_ID, TAPIFACE, "udp"),
831 "init_code": functools.partial(connect_filter_peer,"udp"),
835 "from": (TESTBED_ID, TUNFILTERS, "tcp"),
836 "to": (TESTBED_ID, TUNFILTERS, "tcp"),
837 "init_code": functools.partial(connect_filter_filter,"tcp"),
841 "from": (TESTBED_ID, TUNFILTERS, "udp"),
842 "to": (TESTBED_ID, TUNFILTERS, "udp"),
843 "init_code": functools.partial(connect_filter_filter,"udp"),
847 "from": (TESTBED_ID, TAPFILTERS, "tcp"),
848 "to": (TESTBED_ID, TAPFILTERS, "tcp"),
849 "init_code": functools.partial(connect_filter_filter,"tcp"),
853 "from": (TESTBED_ID, TAPFILTERS, "udp"),
854 "to": (TESTBED_ID, TAPFILTERS, "udp"),
855 "init_code": functools.partial(connect_filter_filter,"udp"),
859 "from": (TESTBED_ID, TUNIFACE, "tcp"),
860 "to": (None, None, "tcp"),
861 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
862 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
866 "from": (TESTBED_ID, TUNIFACE, "udp"),
867 "to": (None, None, "udp"),
868 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
869 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
873 "from": (TESTBED_ID, TUNIFACE, "fd->"),
874 "to": (None, None, "->fd"),
875 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
879 "from": (TESTBED_ID, TUNIFACE, "gre"),
880 "to": (None, None, "gre"),
881 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
885 "from": (TESTBED_ID, TAPIFACE, "tcp"),
886 "to": (None, None, "tcp"),
887 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
888 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
892 "from": (TESTBED_ID, TAPIFACE, "udp"),
893 "to": (None, None, "udp"),
894 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
895 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
899 "from": (TESTBED_ID, TAPIFACE, "fd->"),
900 "to": (None, None, "->fd"),
901 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
904 # EGRE is an extension of PlanetLab, so we can't connect externally
905 # if the other testbed isn't another PlanetLab
907 "from": (TESTBED_ID, TAPIFACE, "gre"),
908 "to": (TESTBED_ID, None, "gre"),
909 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
913 "from": (TESTBED_ID, ALLFILTERS, "tcp"),
914 "to": (None, None, "tcp"),
915 "init_code": functools.partial(crossconnect_filter_peer_init,"tcp"),
916 "compl_code": functools.partial(crossconnect_filter_peer_compl,"tcp"),
920 "from": (TESTBED_ID, ALLFILTERS, "udp"),
921 "to": (None, None, "udp"),
922 "init_code": functools.partial(crossconnect_filter_peer_init,"udp"),
923 "compl_code": functools.partial(crossconnect_filter_peer_compl,"udp"),
929 "forward_X11": dict({
930 "name": "forward_X11",
931 "help": "Forward x11 from main namespace to the node",
932 "type": Attribute.BOOL,
934 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
935 "validation_function": validation.is_bool,
939 "help": "Constrain hostname during resource discovery. May use wildcards.",
940 "type": Attribute.STRING,
941 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
942 "validation_function": validation.is_string,
946 "help": "Constrain location (city) during resource discovery. May use wildcards.",
947 "type": Attribute.STRING,
948 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
949 "validation_function": validation.is_string,
953 "help": "Constrain location (country) during resource discovery. May use wildcards.",
954 "type": Attribute.STRING,
955 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
956 "validation_function": validation.is_string,
960 "help": "Constrain location (region) during resource discovery. May use wildcards.",
961 "type": Attribute.STRING,
962 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
963 "validation_function": validation.is_string,
965 "architecture": dict({
966 "name": "architecture",
967 "help": "Constrain architexture during resource discovery.",
968 "type": Attribute.ENUM,
969 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
970 "allowed": ["x86_64",
972 "validation_function": validation.is_enum,
974 "operating_system": dict({
975 "name": "operatingSystem",
976 "help": "Constrain operating system during resource discovery.",
977 "type": Attribute.ENUM,
978 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
984 "validation_function": validation.is_enum,
988 "help": "Constrain the PlanetLab site this node should reside on.",
989 "type": Attribute.ENUM,
990 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
994 "validation_function": validation.is_enum,
996 "min_reliability": dict({
997 "name": "minReliability",
998 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
999 "type": Attribute.DOUBLE,
1001 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1002 "validation_function": validation.is_number,
1004 "max_reliability": dict({
1005 "name": "maxReliability",
1006 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1007 "type": Attribute.DOUBLE,
1009 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1010 "validation_function": validation.is_number,
1012 "min_bandwidth": dict({
1013 "name": "minBandwidth",
1014 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
1015 "type": Attribute.DOUBLE,
1017 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1018 "validation_function": validation.is_number,
1020 "max_bandwidth": dict({
1021 "name": "maxBandwidth",
1022 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1023 "type": Attribute.DOUBLE,
1025 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1026 "validation_function": validation.is_number,
1030 "help": "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
1031 "type": Attribute.DOUBLE,
1033 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1034 "validation_function": validation.is_number,
1038 "help": "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1039 "type": Attribute.DOUBLE,
1041 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1042 "validation_function": validation.is_number,
1046 "help": "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
1047 "type": Attribute.DOUBLE,
1049 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1050 "validation_function": validation.is_number,
1054 "help": "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1055 "type": Attribute.DOUBLE,
1057 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1058 "validation_function": validation.is_number,
1061 "name": "timeframe",
1062 "help": "Past time period in which to check information about the node. Values are year,month, week, latest",
1063 "type": Attribute.ENUM,
1065 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1066 "allowed": ["latest",
1070 "validation_function": validation.is_enum,
1076 "type": Attribute.BOOL,
1078 "flags": Attribute.NoDefaultValue,
1079 "validation_function": validation.is_bool
1083 "help": "This is the primary interface for the attached node",
1084 "type": Attribute.BOOL,
1086 "validation_function": validation.is_bool
1090 "help": "Device name",
1091 "type": Attribute.STRING,
1092 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1093 "validation_function": validation.is_string
1097 "help": "Maximum transmition unit for device",
1098 "type": Attribute.INTEGER,
1100 "validation_function": validation.is_integer_range(0,1500)
1104 "help": "Network mask for the device (eg: 24 for /24 network)",
1105 "type": Attribute.INTEGER,
1106 "validation_function": validation.is_integer_range(8,24)
1110 "help": "Enable SNAT (source NAT to the internet) no this device",
1111 "type": Attribute.BOOL,
1113 "validation_function": validation.is_bool
1116 "name": "multicast",
1117 "help": "Enable multicast forwarding on this device. "
1118 "Note that you still need a multicast routing daemon "
1120 "type": Attribute.BOOL,
1122 "validation_function": validation.is_bool
1124 "pointopoint": dict({
1125 "name": "pointopoint",
1126 "help": "If the interface is a P2P link, the remote endpoint's IP "
1127 "should be set on this attribute.",
1128 "type": Attribute.STRING,
1129 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1130 "validation_function": validation.is_string
1134 "help": "Emulated transmission speed (in kbytes per second)",
1135 "type": Attribute.INTEGER,
1136 "range" : (1,10*2**20),
1137 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1138 "validation_function": validation.is_integer
1140 "txqueuelen": dict({
1141 "name": "txqueuelen",
1142 "help": "Transmission queue length (in packets)",
1143 "type": Attribute.INTEGER,
1145 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1146 "range" : (1,10000),
1147 "validation_function": validation.is_integer
1152 "help": "Command line string",
1153 "type": Attribute.STRING,
1154 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1155 "validation_function": validation.is_string
1158 "name": "ccnRoutes",
1159 "help": "Route can be static (e.g. udp ip) or multicast (e.g. udp 224.0.0.204 2869). To separate different route use '|' ",
1160 "type": Attribute.STRING,
1161 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1162 "validation_function": validation.is_string
1166 "help": "Run with root privileges",
1167 "type": Attribute.BOOL,
1168 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1170 "validation_function": validation.is_bool
1174 "help": "Standard input",
1175 "type": Attribute.STRING,
1176 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1177 "validation_function": validation.is_string
1182 "help": "Space-separated list of packages required to run the application",
1183 "type": Attribute.STRING,
1184 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1185 "validation_function": validation.is_string
1187 "build-depends": dict({
1188 "name": "buildDepends",
1189 "help": "Space-separated list of packages required to build the application",
1190 "type": Attribute.STRING,
1191 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1192 "validation_function": validation.is_string
1194 "rpm-fusion": dict({
1195 "name": "rpmFusion",
1196 "help": "True if required packages can be found in the RpmFusion repository",
1197 "type": Attribute.BOOL,
1198 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1200 "validation_function": validation.is_bool
1204 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
1205 "Archives won't be expanded automatically.",
1206 "type": Attribute.STRING,
1207 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1208 "validation_function": validation.is_string
1210 "ccnxversion": dict({
1211 "name": "ccnxVersion",
1212 "help": "Version of ccnx source code to install in the node.",
1213 "type": Attribute.ENUM,
1215 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1216 "allowed": ["0.6.0",
1218 "validation_function": validation.is_enum,
1220 "repository": dict({
1221 "name": "repository",
1222 "help": "If True the ccnr command will be issued on start, and a repository will be created.",
1223 "type": Attribute.BOOL,
1225 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1226 "validation_function": validation.is_bool,
1228 "ccnlocalport" : dict({
1229 "name" : "ccnLocalPort",
1230 "help" : "Local port to bind the ccn daemon. (i.e. CCN_LOCAL_PORT=)",
1231 "type" : Attribute.INTEGER,
1232 "flags" : Attribute.DesignInvisible | \
1233 Attribute.ExecInvisible | \
1234 Attribute.ExecImmutable | \
1236 "validation_function" : validation.is_integer,
1240 "help": "Build commands to execute after deploying the sources. "
1241 "Sources will be in the ${SOURCES} folder. "
1242 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
1243 "Try to make the commands return with a nonzero exit code on error.\n"
1244 "Also, do not install any programs here, use the 'install' attribute. This will "
1245 "help keep the built files constrained to the build folder (which may "
1246 "not be the home folder), and will result in faster deployment. Also, "
1247 "make sure to clean up temporary files, to reduce bandwidth usage between "
1248 "nodes when transferring built packages.",
1249 "type": Attribute.STRING,
1250 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1251 "validation_function": validation.is_string
1255 "help": "Commands to transfer built files to their final destinations. "
1256 "Sources will be in the initial working folder, and a special "
1257 "tag ${SOURCES} can be used to reference the experiment's "
1258 "home folder (where the application commands will run).\n"
1259 "ALL sources and targets needed for execution must be copied there, "
1260 "if building has been enabled.\n"
1261 "That is, 'slave' nodes will not automatically get any source files. "
1262 "'slave' nodes don't get build dependencies either, so if you need "
1263 "make and other tools to install, be sure to provide them as "
1264 "actual dependencies instead.",
1265 "type": Attribute.STRING,
1266 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1267 "validation_function": validation.is_string
1270 "netpipe_mode": dict({
1272 "help": "Link mode:\n"
1273 " * SERVER: applies to incoming connections\n"
1274 " * CLIENT: applies to outgoing connections\n"
1275 " * SERVICE: applies to both",
1276 "type": Attribute.ENUM,
1277 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1278 "allowed": ["SERVER",
1281 "validation_function": validation.is_enum,
1285 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
1286 "type": Attribute.STRING,
1287 "validation_function": is_portlist,
1291 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
1292 "type": Attribute.STRING,
1293 "validation_function": is_addrlist,
1297 "help": "Inbound bandwidth limit (in Mbit/s)",
1298 "type": Attribute.DOUBLE,
1299 "validation_function": validation.is_number,
1303 "help": "Outbound bandwidth limit (in Mbit/s)",
1304 "type": Attribute.DOUBLE,
1305 "validation_function": validation.is_number,
1309 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
1310 "type": Attribute.DOUBLE,
1311 "validation_function": validation.is_number,
1315 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
1316 "type": Attribute.DOUBLE,
1317 "validation_function": validation.is_number,
1321 "help": "Inbound packet delay (in milliseconds)",
1322 "type": Attribute.INTEGER,
1324 "validation_function": validation.is_integer,
1328 "help": "Outbound packet delay (in milliseconds)",
1329 "type": Attribute.INTEGER,
1331 "validation_function": validation.is_integer,
1335 "help": "Path to a .c or .py source for a filter module, or a binary .so",
1336 "type": Attribute.STRING,
1337 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1338 "validation_function": validation.is_string
1342 "help": "Module arguments - comma-separated list of name=value pairs",
1343 "type": Attribute.STRING,
1344 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1345 "validation_function": validation.is_string
1347 "routing_algorithm": dict({
1348 "name": "algorithm",
1349 "help": "Routing algorithm.",
1351 "type": Attribute.ENUM,
1352 "allowed": ["dvmrp"],
1353 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1354 "validation_function": validation.is_enum,
1361 "help": "Standard output stream"
1365 "help": "Application standard error",
1369 "help": "Output of the build process",
1372 "netpipe_stats": dict({
1373 "name": "netpipeStats",
1374 "help": "Information about rule match counters, packets dropped, etc.",
1379 "help": "Detailled log of all packets going through the interface",
1383 "help": "PCAP trace of all packets going through the interface",
1387 "help": "Extra output trace for applications. When activated this trace can be referenced with wildcard a reference from an Application command line. Ex: command: 'tcpdump -w {#[elemet-label].trace[trace-id].[name|path]#}' ",
1389 "dropped_stats": dict({
1390 "name": "dropped_stats",
1391 "help": "Information on dropped packets on a filer or queue associated to a network interface",
1393 "queue_stats_f": dict({
1394 "name": "queue_stats_f",
1395 "help": "Detailled, fine-grained information on egress queue state, csv format.",
1397 "queue_stats_b": dict({
1398 "name": "queue_stats_b",
1399 "help": "Detailled, fine-grained information on ingress queue state, csv format.",
1404 INTERNET, NODE, NODEIFACE, CLASSQUEUEFILTER, LOGGINGCLASSQUEUEFILTER, TOSQUEUEFILTER,
1405 MULTICASTANNOUNCER, MULTICASTFORWARDER, MULTICASTROUTER,
1406 TUNFILTER, TAPIFACE, TUNIFACE, NETPIPE,
1407 NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, CCNXDAEMON, APPLICATION ]
1410 INTERNET, Parallel(NODE),
1412 Parallel(MULTICASTANNOUNCER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTROUTER),
1413 Parallel(TAPIFACE), Parallel(TUNIFACE), NETPIPE,
1414 Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(CCNXDAEMON),
1415 Parallel(APPLICATION)]
1417 # Start (and prestart) node after ifaces, because the node needs the ifaces in order to set up routes
1418 start_order = [ INTERNET,
1420 Parallel(TAPIFACE), Parallel(TUNIFACE),
1421 Parallel(NODE), NETPIPE,
1422 Parallel(MULTICASTANNOUNCER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTROUTER),
1423 Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(CCNXDAEMON),
1424 Parallel(APPLICATION)]
1428 Parallel(APPLICATION),
1429 Parallel (CCNXDAEMON),
1430 Parallel(MULTICASTROUTER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTANNOUNCER),
1431 Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NETPIPE),
1432 Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY),
1433 NODEIFACE, Parallel(NODE) ]
1435 factories_info = dict({
1437 "help": "Virtualized Node (V-Server style)",
1438 "category": FC.CATEGORY_NODES,
1439 "create_function": create_node,
1440 "preconfigure_function": configure_node,
1441 "prestart_function": configure_node_routes,
1458 # NEPI-in-NEPI attributes
1459 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
1461 "connector_types": ["devs", "apps", "pipes", "deps"],
1462 "tags": [tags.NODE, tags.ALLOW_ROUTES],
1465 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
1466 "category": FC.CATEGORY_DEVICES,
1467 "create_function": create_nodeiface,
1468 "preconfigure_function": configure_nodeiface,
1469 "box_attributes": [ ],
1470 "connector_types": ["node", "inet"],
1471 "tags": [tags.INTERFACE, tags.HAS_ADDRESSES],
1474 "help": "Virtual TUN network interface (layer 3)",
1475 "category": FC.CATEGORY_DEVICES,
1476 "create_function": create_tuniface,
1477 "preconfigure_function": preconfigure_tuniface,
1478 "configure_function": postconfigure_tuniface,
1479 "prestart_function": prestart_tuniface,
1481 "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
1483 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1485 "traces": ["packets", "pcap"],
1486 "connector_types": ["node","udp","tcp","fd->","gre"],
1487 "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
1490 "help": "Virtual TAP network interface (layer 2)",
1491 "category": FC.CATEGORY_DEVICES,
1492 "create_function": create_tapiface,
1493 "preconfigure_function": preconfigure_tuniface,
1494 "configure_function": postconfigure_tuniface,
1495 "prestart_function": prestart_tuniface,
1497 "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
1499 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1501 "traces": ["packets", "pcap"],
1502 "connector_types": ["node","udp","tcp","fd->","gre"],
1503 "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
1506 "help": "TUN/TAP stream filter\n\n"
1507 "If specified, it should be either a .py or .so module. "
1508 "It will be loaded, and all incoming and outgoing packets "
1509 "will be routed through it. The filter will not be responsible "
1510 "for buffering, packet queueing is performed in tun_connect "
1511 "already, so it should not concern itself with it. It should "
1512 "not, however, block in one direction if the other is congested.\n"
1514 "Modules are expected to have the following methods:\n"
1516 "\t\tIf arguments are given, this method will be called with the\n"
1517 "\t\tgiven arguments (as keyword args in python modules, or a single\n"
1518 "\taccept_packet(packet, direction):\n"
1519 "\t\tDecide whether to drop the packet. Direction is 0 for packets "
1520 "coming from the local side to the remote, and 1 is for packets "
1521 "coming from the remote side to the local. Return a boolean, "
1522 "true if the packet is not to be dropped.\n"
1523 "\tfilter_init():\n"
1524 "\t\tInitializes a filtering pipe (filter_run). It should "
1525 "return two file descriptors to use as a bidirectional "
1526 "pipe: local and remote. 'local' is where packets from the "
1527 "local side will be written to. After filtering, those packets "
1528 "should be written to 'remote', where tun_connect will read "
1529 "from, and it will forward them to the remote peer. "
1530 "Packets from the remote peer will be written to 'remote', "
1531 "where the filter is expected to read from, and eventually "
1532 "forward them to the local side. If the file descriptors are "
1533 "not nonblocking, they will be set to nonblocking. So it's "
1534 "better to set them from the start like that.\n"
1535 "\tfilter_run(local, remote):\n"
1536 "\t\tIf filter_init is provided, it will be called repeatedly, "
1537 "in a separate thread until the process is killed. It should "
1538 "sleep at most for a second.\n"
1539 "\tfilter_close(local, remote):\n"
1540 "\t\tCalled then the process is killed, if filter_init was provided. "
1541 "It should, among other things, close the file descriptors.\n"
1543 "Python modules are expected to return a tuple in filter_init, "
1544 "either of file descriptors or file objects, while native ones "
1545 "will receive two int*.\n"
1547 "Python modules can additionally contain a custom queue class "
1548 "that will replace the FIFO used by default. The class should "
1549 "be named 'queueclass' and contain an interface compatible with "
1550 "collections.deque. That is, indexing (especiall for q[0]), "
1551 "bool(q), popleft, appendleft, pop (right), append (right), "
1552 "len(q) and clear.",
1553 "category": FC.CATEGORY_CHANNELS,
1554 "create_function": create_tunfilter,
1557 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1559 "connector_types": ["->fd","udp","tcp"],
1561 CLASSQUEUEFILTER : dict({
1562 "help": "TUN classful queue, uses a separate queue for each user-definable class.\n\n"
1563 "It takes two arguments, both of which have sensible defaults:\n"
1564 "\tsize: the base size of each class' queue\n"
1565 "\tclasses: the class definitions, which follow the following syntax:\n"
1566 '\t <CLASSLIST> ::= <CLASS> ":" CLASSLIST\n'
1568 '\t <CLASS> ::= <PROTOLIST> "*" <PRIORITYSPEC>\n'
1569 '\t | <DFLTCLASS>\n'
1570 '\t <DFLTCLASS> ::= "*" <PRIORITYSPEC>\n'
1571 '\t <PROTOLIST> ::= <PROTO> "." <PROTOLIST>\n'
1573 '\t <PROTO> ::= <NAME> | <NUMBER>\n'
1574 '\t <NAME> ::= --see http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers --\n'
1575 '\t --only in lowercase, with special characters removed--\n'
1576 '\t --or see below--\n'
1577 '\t <NUMBER> ::= [0-9]+\n'
1578 '\t <PRIORITYSPEC> ::= <THOUGHPUT> [ "#" <SIZE> ] [ "p" <PRIORITY> ]\n'
1579 '\t <THOUGHPUT> ::= NUMBER -- default 1\n'
1580 '\t <PRIORITY> ::= NUMBER -- default 0\n'
1581 '\t <SIZE> ::= NUMBER -- default 1\n'
1583 "Size, thoughput and priority are all relative terms. "
1584 "Sizes are multipliers for the size argument, thoughput "
1585 "is applied relative to other classes and the same with "
1587 "category": FC.CATEGORY_CHANNELS,
1588 "create_function": create_classqueuefilter,
1591 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1593 "connector_types": ["->fd","udp","tcp"],
1594 "traces": ["dropped_stats"],
1596 LOGGINGCLASSQUEUEFILTER : dict({
1597 "help": "TUN classful queue, uses a separate queue for each user-definable class.\n"
1598 "See ClassQueueFilter. This version adds detailled queue state tracing.",
1599 "category": FC.CATEGORY_CHANNELS,
1600 "create_function": create_loggingclassqueuefilter,
1603 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1605 "connector_types": ["->fd","udp","tcp"],
1606 "traces": ["dropped_stats","queue_stats_f","queue_stats_b"],
1608 TOSQUEUEFILTER : dict({
1609 "help": "TUN classfull queue that classifies according to the TOS (RFC 791) IP field.\n\n"
1610 "It takes a size argument that specifies the size of each class. As TOS is a "
1611 "subset of DiffServ, this queue half-implements DiffServ.",
1612 "category": FC.CATEGORY_CHANNELS,
1613 "create_function": create_tosqueuefilter,
1616 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1618 "connector_types": ["->fd","udp","tcp"],
1622 "help": "Generic executable command line application",
1623 "category": FC.CATEGORY_APPLICATIONS,
1624 "create_function": create_application,
1625 "start_function": start_application,
1626 "status_function": status_application,
1627 "stop_function": stop_application,
1628 "configure_function": configure_application,
1629 "box_attributes": ["command", "sudo", "stdin",
1630 "depends", "build-depends", "build", "install",
1631 "sources", "rpm-fusion" ],
1632 "connector_types": ["node"],
1633 "traces": ["stdout", "stderr", "buildlog", "output"],
1634 "tags": [tags.APPLICATION],
1638 "help": "CCNx daemon",
1639 "category": FC.CATEGORY_APPLICATIONS,
1640 "create_function": create_ccnxdaemon,
1641 "prestart_function": prestart_ccnxdaemon,
1642 "status_function": status_application,
1643 "stop_function": stop_application,
1644 "configure_function": configure_application,
1645 "box_attributes": ["ccnroutes", "build", "ccnlocalport",
1646 "install", "ccnxversion", "sources", "repository"],
1647 "connector_types": ["node"],
1648 "traces": ["stdout", "stderr", "buildlog", "output"],
1649 "tags": [tags.APPLICATION],
1652 "help": "Requirement for package or application to be installed on some node",
1653 "category": FC.CATEGORY_APPLICATIONS,
1654 "create_function": create_dependency,
1655 "preconfigure_function": configure_dependency,
1656 "status_function": status_dependency,
1657 "box_attributes": ["depends", "build-depends", "build", "install",
1658 "sources", "rpm-fusion" ],
1659 "connector_types": ["node"],
1660 "traces": ["buildlog"],
1662 NEPIDEPENDENCY: dict({
1663 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
1664 "category": FC.CATEGORY_APPLICATIONS,
1665 "create_function": create_nepi_dependency,
1666 "preconfigure_function": configure_dependency,
1667 "box_attributes": [],
1668 "connector_types": ["node"],
1669 "traces": ["buildlog"],
1671 NS3DEPENDENCY: dict({
1672 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
1673 "category": FC.CATEGORY_APPLICATIONS,
1674 "create_function": create_ns3_dependency,
1675 "preconfigure_function": configure_dependency,
1676 "box_attributes": [ ],
1677 "connector_types": ["node"],
1678 "traces": ["buildlog"],
1680 MULTICASTFORWARDER: dict({
1681 "help": "This application installs a userspace packet forwarder "
1682 "that, when connected to a node, filters all packets "
1683 "flowing through multicast-capable virtual interfaces "
1684 "and applies custom-specified routing policies.",
1685 "category": FC.CATEGORY_APPLICATIONS,
1686 "create_function": create_multicast_forwarder,
1687 "preconfigure_function": configure_forwarder,
1688 "start_function": start_application,
1689 "status_function": status_application,
1690 "stop_function": stop_application,
1691 "box_attributes": [ ],
1692 "connector_types": ["node","router"],
1693 "traces": ["buildlog","stderr"],
1695 MULTICASTANNOUNCER: dict({
1696 "help": "This application installs a userspace daemon that "
1697 "monitors multicast membership and announces it on all "
1698 "multicast-capable interfaces.\n"
1699 "This does not usually happen automatically on PlanetLab slivers.",
1700 "category": FC.CATEGORY_APPLICATIONS,
1701 "create_function": create_multicast_announcer,
1702 "preconfigure_function": configure_announcer,
1703 "start_function": start_application,
1704 "status_function": status_application,
1705 "stop_function": stop_application,
1706 "box_attributes": [ ],
1707 "connector_types": ["node"],
1708 "traces": ["buildlog","stderr"],
1710 MULTICASTROUTER: dict({
1711 "help": "This application installs a userspace daemon that "
1712 "monitors multicast membership and announces it on all "
1713 "multicast-capable interfaces.\n"
1714 "This does not usually happen automatically on PlanetLab slivers.",
1715 "category": FC.CATEGORY_APPLICATIONS,
1716 "create_function": create_multicast_router,
1717 "preconfigure_function": configure_router,
1718 "start_function": start_application,
1719 "status_function": status_application,
1720 "stop_function": stop_application,
1721 "box_attributes": ["routing_algorithm"],
1722 "connector_types": ["fwd"],
1723 "traces": ["buildlog","stdout","stderr"],
1726 "help": "Internet routing",
1727 "category": FC.CATEGORY_CHANNELS,
1728 "create_function": create_internet,
1729 "connector_types": ["devs"],
1730 "tags": [tags.INTERNET],
1733 "help": "Link emulation",
1734 "category": FC.CATEGORY_CHANNELS,
1735 "create_function": create_netpipe,
1736 "configure_function": configure_netpipe,
1737 "box_attributes": ["netpipe_mode",
1738 "addr_list", "port_list",
1739 "bw_in","plr_in","delay_in",
1740 "bw_out","plr_out","delay_out"],
1741 "connector_types": ["node"],
1742 "traces": ["netpipe_stats"],
1746 testbed_attributes = dict({
1749 "help": "The hierarchical Resource Name (HRN) for the PlanetLab slice.",
1750 "type": Attribute.STRING,
1751 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1752 "validation_function": validation.is_string
1756 "help": "Activates the use of SFA for node reservation.",
1757 "type": Attribute.BOOL,
1758 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1759 "validation_function": validation.is_bool
1763 "help": "The name of the PlanetLab slice to use",
1764 "type": Attribute.STRING,
1765 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1766 "validation_function": validation.is_string
1770 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
1771 "type": Attribute.STRING,
1772 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1773 "validation_function": validation.is_string
1777 "help": "The PlanetLab user's password.",
1778 "type": Attribute.STRING,
1779 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1780 "validation_function": validation.is_string
1784 "help": "The PlanetLab PLC API host",
1785 "type": Attribute.STRING,
1786 "value": "www.planet-lab.eu",
1787 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1788 "validation_function": validation.is_string
1792 "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.",
1793 "type": Attribute.STRING,
1794 "value": "https://%(hostname)s:443/PLCAPI/",
1795 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1796 "validation_function": validation.is_string
1800 "help": "Https proxy to connect to the outside world",
1801 "type": Attribute.STRING,
1802 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1803 "validation_function": validation.is_string
1805 "p2p_deployment": dict({
1806 "name": "p2pDeployment",
1807 "help": "Enable peer-to-peer deployment of applications and dependencies. "
1808 "When enabled, dependency packages and applications are "
1809 "deployed in a P2P fashion, picking a single node to do "
1810 "the building or repo download, while all the others "
1811 "cooperatively exchange resulting binaries or rpms. "
1812 "When deploying to many nodes, this is a far more efficient "
1813 "use of resources. It does require re-encrypting and distributing "
1814 "the slice's private key. Though it is implemented in a secure "
1815 "fashion, if they key's sole purpose is not PlanetLab, then this "
1816 "feature should be disabled.",
1817 "type": Attribute.BOOL,
1819 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1820 "validation_function": validation.is_bool
1822 "slice_ssh_key": dict({
1823 "name": "sliceSSHKey",
1824 "help": "The controller-local path to the slice user's ssh private key. "
1825 "It is the user's responsability to deploy this file where the controller "
1826 "will run, it won't be done automatically because it's sensitive information. "
1827 "It is recommended that a NEPI-specific user be created for this purpose and "
1828 "this purpose alone.",
1829 "type": Attribute.STRING,
1830 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1831 "validation_function": validation.is_string
1833 "pl_log_level": dict({
1834 "name": "plLogLevel",
1835 "help": "Verbosity of logging of planetlab events.",
1837 "type": Attribute.ENUM,
1838 "allowed": ["DEBUG",
1843 "validation_function": validation.is_enum,
1845 "tap_port_base": dict({
1846 "name": "tapPortBase",
1847 "help": "Base port to use when connecting TUN/TAPs. Effective port will be BASE + GUID.",
1848 "type": Attribute.INTEGER,
1850 "range": (2000,30000),
1851 "validation_function": validation.is_integer_range(2000,30000)
1853 "clean_proc": dict({
1854 "name": "cleanProc",
1855 "help": "Set to True if the slice will be dedicated to this experiment. "
1856 "NEPI will perform node and slice process cleanup, making sure slices are "
1857 "in a clean, repeatable state before running the experiment.",
1858 "type": Attribute.BOOL,
1860 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1861 "validation_function": validation.is_bool
1863 "clean_home": dict({
1864 "name": "cleanHome",
1865 "help": "Set to True all preexistent directories in the home "
1866 "directory of each sliver will be removed before the "
1867 "start of the experiment.",
1868 "type": Attribute.BOOL,
1870 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1871 "validation_function": validation.is_bool
1875 supported_recovery_policies = [
1881 class MetadataInfo(metadata.MetadataInfo):
1883 def connector_types(self):
1884 return connector_types
1887 def connections(self):
1891 def attributes(self):
1899 def create_order(self):
1903 def configure_order(self):
1904 return configure_order
1907 def prestart_order(self):
1911 def start_order(self):
1915 def factories_info(self):
1916 return factories_info
1919 def testbed_attributes(self):
1920 return testbed_attributes
1923 def testbed_id(self):
1927 def testbed_version(self):
1928 return TESTBED_VERSION
1931 def supported_recovery_policies(self):
1932 return supported_recovery_policies