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 TOSQUEUEFILTER = "TosQueueFilter"
34 MULTICASTFORWARDER = "MulticastForwarder"
35 MULTICASTANNOUNCER = "MulticastAnnouncer"
36 MULTICASTROUTER = "MulticastRouter"
38 TUNFILTERS = (TUNFILTER, CLASSQUEUEFILTER, TOSQUEUEFILTER)
39 TAPFILTERS = (TUNFILTER, )
40 ALLFILTERS = (TUNFILTER, CLASSQUEUEFILTER, TOSQUEUEFILTER)
42 PL_TESTBED_ID = "planetlab"
45 ### Custom validation functions ###
46 def is_addrlist(attribute, value):
47 if not validation.is_string(attribute, value):
54 components = value.split(',')
56 for component in components:
58 addr, mask = component.split('/',1)
60 addr, mask = component, '32'
62 if mask is not None and not (mask and mask.isdigit()):
63 # No empty or nonnumeric masks
66 if not validation.is_ip4_address(attribute, addr):
67 # Address part must be ipv4
72 def is_portlist(attribute, value):
73 if not validation.is_string(attribute, value):
80 components = value.split(',')
82 for component in components:
84 pfrom, pto = component.split('-',1)
86 pfrom = pto = component
88 if not pfrom or not pto or not pfrom.isdigit() or not pto.isdigit():
89 # No empty or nonnumeric ports
95 ### Connection functions ####
97 def connect_node_iface_node(testbed_instance, node_guid, iface_guid):
98 node = testbed_instance._elements[node_guid]
99 iface = testbed_instance._elements[iface_guid]
102 def connect_node_iface_inet(testbed_instance, iface_guid, inet_guid):
103 iface = testbed_instance._elements[iface_guid]
104 iface.has_internet = True
106 def connect_tun_iface_node(testbed_instance, node_guid, iface_guid):
107 node = testbed_instance._elements[node_guid]
108 iface = testbed_instance._elements[iface_guid]
110 node.required_vsys.update(('fd_tuntap', 'vif_up', 'vif_down'))
111 node.required_packages.update(('python', 'python-crypto', 'python-setuptools', 'gcc'))
113 def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid):
114 iface = testbed_instance._elements[iface_guid]
115 peer_iface = testbed_instance._elements[peer_iface_guid]
116 iface.peer_iface = peer_iface
117 peer_iface.peer_iface = iface
120 peer_iface.peer_proto = \
121 peer_iface.tun_proto = proto
122 iface.tun_key = peer_iface.tun_key
124 def connect_tun_iface_filter(testbed_instance, iface_guid, filter_guid):
125 iface = testbed_instance._elements[iface_guid]
126 filt = testbed_instance._elements[filter_guid]
127 traces = testbed_instance._get_traces(filter_guid)
128 if 'dropped_stats' in traces:
129 args = filt.args if filt.args else ""
130 filt.args = ','.join(filt.args.split(',') + ["logdropped=true",])
131 iface.filter_module = filt
132 filt.iface_guid = iface_guid
133 filt.iface = weakref.ref(iface)
136 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
138 def connect_filter_peer(proto, testbed_instance, filter_guid, peer_guid):
139 peer = testbed_instance._elements[peer_guid]
140 filt = testbed_instance._elements[filter_guid]
141 filt.peer_proto = proto
142 filt.peer_guid = peer_guid
144 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
146 def connect_filter_filter(proto, testbed_instance, filter_guid, peer_guid):
147 peer = testbed_instance._elements[peer_guid]
148 filt = testbed_instance._elements[filter_guid]
149 filt.peer_proto = proto
150 peer.peer_proto = proto
152 peer.peer_guid = filt.iface_guid
154 filt.peer_guid = peer.iface_guid
155 if filt.iface_guid and filt.peer_guid:
156 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
158 def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data):
159 iface = testbed_instance._elements[iface_guid]
160 iface.peer_iface = None
161 iface.peer_addr = peer_iface_data.get("tun_addr")
162 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
163 iface.peer_port = peer_iface_data.get("tun_port")
164 iface.peer_cipher = peer_iface_data.get("tun_cipher")
165 iface.tun_key = min(iface.tun_key, peer_iface_data.get("tun_key"))
166 iface.tun_proto = proto
168 preconfigure_tuniface(testbed_instance, iface_guid)
170 def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data):
171 # refresh (refreshable) attributes for second-phase
172 iface = testbed_instance._elements[iface_guid]
173 iface.peer_addr = peer_iface_data.get("tun_addr")
174 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
175 iface.peer_port = peer_iface_data.get("tun_port")
176 iface.peer_cipher = peer_iface_data.get("tun_cipher")
178 postconfigure_tuniface(testbed_instance, iface_guid)
180 def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_iface_data):
181 crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
182 crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
184 def crossconnect_filter_peer_init(proto, testbed_instance, filter_guid, peer_data):
185 filt = testbed_instance._elements[filter_guid]
186 filt.peer_proto = proto
187 crossconnect_tun_iface_peer_init(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
189 def crossconnect_filter_peer_compl(proto, testbed_instance, filter_guid, peer_data):
190 filt = testbed_instance._elements[filter_guid]
191 filt.peer_proto = proto
192 crossconnect_tun_iface_peer_compl(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
194 def crossconnect_filter_peer_both(proto, testbed_instance, filter_guid, peer_data):
195 crossconnect_filter_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
196 crossconnect_filter_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
198 def connect_dep(testbed_instance, node_guid, app_guid, node=None, app=None):
199 node = node or testbed_instance._elements[node_guid]
200 app = app or testbed_instance._elements[app_guid]
204 node.required_packages.update(set(
205 app.depends.split() ))
208 if app.home_path and app.home_path not in node.pythonpath:
209 node.pythonpath.append(app.home_path)
212 for envkey, envval in app.env.iteritems():
213 envval = app._replace_paths(envval)
214 node.env[envkey].append(envval)
217 node.rpmFusion = True
219 def connect_forwarder(testbed_instance, node_guid, fwd_guid):
220 node = testbed_instance._elements[node_guid]
221 fwd = testbed_instance._elements[fwd_guid]
222 node.multicast_forwarder = fwd
225 connect_dep(testbed_instance, node_guid, None, app=fwd.router)
227 connect_dep(testbed_instance, node_guid, fwd_guid)
229 def connect_router(testbed_instance, fwd_guid, router_guid):
230 fwd = testbed_instance._elements[fwd_guid]
231 router = testbed_instance._elements[router_guid]
235 connect_dep(testbed_instance, None, router_guid, node=fwd.node)
237 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
238 node = testbed_instance._elements[node_guid]
239 netpipe = testbed_instance._elements[netpipe_guid]
241 node.required_vsys.add('ipfw-be')
242 node.required_packages.add('ipfwslice')
245 ### Creation functions ###
247 def create_node(testbed_instance, guid):
248 parameters = testbed_instance._get_parameters(guid)
250 # create element with basic attributes
251 element = testbed_instance._make_node(parameters)
253 # add constraint on number of (real) interfaces
254 # by counting connected devices
255 dev_guids = testbed_instance.get_connected(guid, "devs", "node")
256 num_open_ifaces = sum( # count True values
257 NODEIFACE == testbed_instance._get_factory_id(guid)
258 for guid in dev_guids )
259 element.min_num_external_ifaces = num_open_ifaces
261 # require vroute vsys if we have routes to set up
262 routes = testbed_instance._add_route.get(guid)
264 vsys = element.routing_method(routes,
265 testbed_instance.vsys_vnet)
266 element.required_vsys.add(vsys)
268 testbed_instance.elements[guid] = element
270 def create_nodeiface(testbed_instance, guid):
271 parameters = testbed_instance._get_parameters(guid)
272 element = testbed_instance._make_node_iface(parameters)
273 testbed_instance.elements[guid] = element
275 def create_tuniface(testbed_instance, guid):
276 parameters = testbed_instance._get_parameters(guid)
277 element = testbed_instance._make_tun_iface(parameters)
279 # Set custom addresses, if there are any already
280 # Setting this early helps set up P2P links
281 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
282 addresses = testbed_instance._add_address[guid]
283 for address in addresses:
284 (address, netprefix, broadcast) = address
285 element.add_address(address, netprefix, broadcast)
287 testbed_instance.elements[guid] = element
289 def create_tapiface(testbed_instance, guid):
290 parameters = testbed_instance._get_parameters(guid)
291 element = testbed_instance._make_tap_iface(parameters)
293 # Set custom addresses, if there are any already
294 # Setting this early helps set up P2P links
295 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
296 addresses = testbed_instance._add_address[guid]
297 for address in addresses:
298 (address, netprefix, broadcast) = address
299 element.add_address(address, netprefix, broadcast)
301 testbed_instance.elements[guid] = element
303 def create_tunfilter(testbed_instance, guid):
304 parameters = testbed_instance._get_parameters(guid)
305 element = testbed_instance._make_tun_filter(parameters)
306 testbed_instance.elements[guid] = element
308 def create_classqueuefilter(testbed_instance, guid):
309 parameters = testbed_instance._get_parameters(guid)
310 element = testbed_instance._make_class_queue_filter(parameters)
311 testbed_instance.elements[guid] = element
313 def create_tosqueuefilter(testbed_instance, guid):
314 parameters = testbed_instance._get_parameters(guid)
315 element = testbed_instance._make_tos_queue_filter(parameters)
316 testbed_instance.elements[guid] = element
318 def create_application(testbed_instance, guid):
319 parameters = testbed_instance._get_parameters(guid)
320 element = testbed_instance._make_application(parameters)
322 # Just inject configuration stuff
323 element.home_path = "nepi-app-%s" % (guid,)
325 testbed_instance.elements[guid] = element
327 def create_ccnxdaemon(testbed_instance, guid):
328 parameters = testbed_instance._get_parameters(guid)
329 element = testbed_instance._make_application(parameters,
330 clazz = testbed_instance._app.CCNxDaemon )
332 # Just inject configuration stuff
333 element.home_path = "nepi-ccnd-%s" % (guid,)
335 testbed_instance.elements[guid] = element
337 def create_dependency(testbed_instance, guid):
338 parameters = testbed_instance._get_parameters(guid)
339 element = testbed_instance._make_dependency(parameters)
341 # Just inject configuration stuff
342 element.home_path = "nepi-dep-%s" % (guid,)
344 testbed_instance.elements[guid] = element
346 def create_nepi_dependency(testbed_instance, guid):
347 parameters = testbed_instance._get_parameters(guid)
348 element = testbed_instance._make_nepi_dependency(parameters)
350 # Just inject configuration stuff
351 element.home_path = "nepi-nepi-%s" % (guid,)
353 testbed_instance.elements[guid] = element
355 def create_ns3_dependency(testbed_instance, guid):
356 parameters = testbed_instance._get_parameters(guid)
357 element = testbed_instance._make_ns3_dependency(parameters)
359 # Just inject configuration stuff
360 element.home_path = "nepi-ns3-%s" % (guid,)
362 testbed_instance.elements[guid] = element
364 def create_multicast_forwarder(testbed_instance, guid):
365 parameters = testbed_instance._get_parameters(guid)
366 element = testbed_instance._make_multicast_forwarder(parameters)
368 # Just inject configuration stuff
369 element.home_path = "nepi-mcfwd-%s" % (guid,)
371 testbed_instance.elements[guid] = element
373 def create_multicast_announcer(testbed_instance, guid):
374 parameters = testbed_instance._get_parameters(guid)
375 element = testbed_instance._make_multicast_announcer(parameters)
377 # Just inject configuration stuff
378 element.home_path = "nepi-mcann-%s" % (guid,)
380 testbed_instance.elements[guid] = element
382 def create_multicast_router(testbed_instance, guid):
383 parameters = testbed_instance._get_parameters(guid)
384 element = testbed_instance._make_multicast_router(parameters)
386 # Just inject configuration stuff
387 element.home_path = "nepi-mcrt-%s" % (guid,)
389 testbed_instance.elements[guid] = element
391 def create_internet(testbed_instance, guid):
392 parameters = testbed_instance._get_parameters(guid)
393 element = testbed_instance._make_internet(parameters)
394 testbed_instance.elements[guid] = element
396 def create_netpipe(testbed_instance, guid):
397 parameters = testbed_instance._get_parameters(guid)
398 element = testbed_instance._make_netpipe(parameters)
399 testbed_instance.elements[guid] = element
401 ### Start/Stop functions ###
403 def prestart_ccnxdaemon(testbed_instance, guid):
404 # ccnx daemon needs to start before the rest of the
406 start_application(testbed_instance, guid)
408 def stop_ccndaemon(testbed_instance, guid):
409 app = testbed_instance.elements[guid]
412 def start_application(testbed_instance, guid):
413 parameters = testbed_instance._get_parameters(guid)
414 traces = testbed_instance._get_traces(guid)
415 app = testbed_instance.elements[guid]
417 app.stdout = "stdout" in traces
418 app.stderr = "stderr" in traces
419 app.buildlog = "buildlog" in traces
420 app.outout = "output" in traces
424 def stop_application(testbed_instance, guid):
425 app = testbed_instance.elements[guid]
428 ### Status functions ###
430 def status_application(testbed_instance, guid):
431 if guid not in testbed_instance.elements.keys():
432 return AS.STATUS_NOT_STARTED
434 app = testbed_instance.elements[guid]
437 ### Configure functions ###
439 def configure_nodeiface(testbed_instance, guid):
440 element = testbed_instance._elements[guid]
442 # Cannot explicitly configure addresses
443 if guid in testbed_instance._add_address:
444 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
447 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
448 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
449 siblings = [ self._element[dev_guid]
450 for dev_guid in dev_guids
451 if dev_guid != guid ]
453 # Fetch address from PLC api
454 element.pick_iface(siblings)
456 # Do some validations
459 def preconfigure_tuniface(testbed_instance, guid):
460 element = testbed_instance._elements[guid]
462 # Set custom addresses if any, and if not set already
463 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
464 addresses = testbed_instance._add_address[guid]
465 for address in addresses:
466 (address, netprefix, broadcast) = address
467 element.add_address(address, netprefix, broadcast)
469 # Link to external interface, if any
470 for iface in testbed_instance._elements.itervalues():
471 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
472 element.external_iface = iface
475 # Set standard TUN attributes
476 if (not element.tun_addr or not element.tun_port) and element.external_iface:
477 element.tun_addr = element.external_iface.address
478 element.tun_port = testbed_instance.tapPortBase + int(guid)
481 traces = testbed_instance._get_traces(guid)
482 for capmode in ('pcap', 'packets'):
483 if capmode in traces:
484 element.capture = capmode
487 element.capture = False
489 # Do some validations
493 element.prepare('tun-%s' % (guid,))
495 def postconfigure_tuniface(testbed_instance, guid):
496 element = testbed_instance._elements[guid]
501 def prestart_tuniface(testbed_instance, guid):
502 element = testbed_instance._elements[guid]
507 def configure_node(testbed_instance, guid):
508 node = testbed_instance._elements[guid]
510 # Just inject configuration stuff
511 node.home_path = "nepi-node-%s" % (guid,)
512 node.ident_path = testbed_instance.sliceSSHKey
513 node.slicename = testbed_instance.slicename
515 # Do some validations
518 # this will be done in parallel in all nodes
519 # this call only spawns the process
520 node.install_dependencies()
522 def configure_node_routes(testbed_instance, guid):
523 node = testbed_instance._elements[guid]
524 routes = testbed_instance._add_route.get(guid)
528 for dev_guid in testbed_instance.get_connected(guid, "devs", "node")
529 for dev in ( testbed_instance._elements.get(dev_guid) ,)
530 if dev and isinstance(dev, testbed_instance._interfaces.TunIface) ]
532 vsys = testbed_instance.vsys_vnet
534 node.configure_routes(routes, devs, vsys)
536 def configure_application(testbed_instance, guid):
537 app = testbed_instance._elements[guid]
539 # Do some validations
542 # Wait for dependencies
543 app.node.wait_dependencies()
548 def configure_dependency(testbed_instance, guid):
549 dep = testbed_instance._elements[guid]
551 # Do some validations
554 # Wait for dependencies
555 dep.node.wait_dependencies()
560 def configure_announcer(testbed_instance, guid):
562 fwd = testbed_instance._elements[guid]
564 for node_guid in testbed_instance.get_connected(guid, "node", "apps")
565 for dev_guid in testbed_instance.get_connected(node_guid, "devs", "node")
566 for dev in ( testbed_instance._elements.get(dev_guid) ,)
567 if dev and isinstance(dev, testbed_instance._interfaces.TunIface)
571 configure_dependency(testbed_instance, guid)
573 def configure_forwarder(testbed_instance, guid):
574 configure_announcer(testbed_instance, guid)
576 # Link ifaces to forwarder
577 fwd = testbed_instance._elements[guid]
578 for iface in fwd.ifaces:
579 iface.multicast_forwarder = '/var/run/mcastfwd'
581 def configure_router(testbed_instance, guid):
583 rt = testbed_instance._elements[guid]
585 for fwd_guid in testbed_instance.get_connected(guid, "fwd", "router")
586 for node_guid in testbed_instance.get_connected(fwd_guid, "node", "apps")
587 for dev_guid in testbed_instance.get_connected(node_guid, "devs", "node")
588 for dev in ( testbed_instance._elements.get(dev_guid) ,)
589 if dev and isinstance(dev, testbed_instance._interfaces.TunIface)
590 and not dev.multicast ]
593 configure_dependency(testbed_instance, guid)
595 def configure_netpipe(testbed_instance, guid):
596 netpipe = testbed_instance._elements[guid]
598 # Do some validations
601 # Wait for dependencies
602 netpipe.node.wait_dependencies()
607 ### Factory information ###
609 connector_types = dict({
611 "help": "Connector from node to applications",
617 "help": "Connector from node to network interfaces",
623 "help": "Connector from node to application dependencies "
624 "(packages and applications that need to be installed)",
630 "help": "Connector from network interfaces to the internet",
636 "help": "Connector to a Node",
642 "help": "Connector to a routing daemon",
648 "help": "Forwarder this routing daemon communicates with",
654 "help": "Connector to a NetPipe",
661 "help": "ip-ip tunneling over TCP link",
667 "help": "ip-ip tunneling over UDP datagrams",
673 "help": "IP or Ethernet tunneling using the GRE protocol",
679 "help": "TUN device file descriptor provider",
685 "help": "TUN device file descriptor slot",
694 "from": (TESTBED_ID, NODE, "devs"),
695 "to": (TESTBED_ID, NODEIFACE, "node"),
696 "init_code": connect_node_iface_node,
700 "from": (TESTBED_ID, NODE, "devs"),
701 "to": (TESTBED_ID, TUNIFACE, "node"),
702 "init_code": connect_tun_iface_node,
706 "from": (TESTBED_ID, NODE, "devs"),
707 "to": (TESTBED_ID, TAPIFACE, "node"),
708 "init_code": connect_tun_iface_node,
712 "from": (TESTBED_ID, NODEIFACE, "inet"),
713 "to": (TESTBED_ID, INTERNET, "devs"),
714 "init_code": connect_node_iface_inet,
718 "from": (TESTBED_ID, NODE, "apps"),
719 "to": (TESTBED_ID, (APPLICATION, CCNXDAEMON, MULTICASTANNOUNCER), "node"),
720 "init_code": connect_dep,
724 "from": (TESTBED_ID, NODE, "deps"),
725 "to": (TESTBED_ID, (DEPENDENCY, NEPIDEPENDENCY, NS3DEPENDENCY), "node"),
726 "init_code": connect_dep,
730 "from": (TESTBED_ID, NODE, "pipes"),
731 "to": (TESTBED_ID, NETPIPE, "node"),
732 "init_code": connect_node_netpipe,
736 "from": (TESTBED_ID, NODE, "apps"),
737 "to": (TESTBED_ID, MULTICASTFORWARDER, "node"),
738 "init_code": connect_forwarder,
742 "from": (TESTBED_ID, MULTICASTFORWARDER, "router"),
743 "to": (TESTBED_ID, MULTICASTROUTER, "fwd"),
744 "init_code": connect_router,
748 "from": (TESTBED_ID, TUNIFACE, "tcp"),
749 "to": (TESTBED_ID, TUNIFACE, "tcp"),
750 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
754 "from": (TESTBED_ID, TUNIFACE, "udp"),
755 "to": (TESTBED_ID, TUNIFACE, "udp"),
756 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
760 "from": (TESTBED_ID, TUNIFACE, "gre"),
761 "to": (TESTBED_ID, TUNIFACE, "gre"),
762 "init_code": functools.partial(connect_tun_iface_peer,"gre"),
766 "from": (TESTBED_ID, TUNIFACE, "fd->"),
767 "to": (TESTBED_ID, TUNFILTERS, "->fd"),
768 "init_code": connect_tun_iface_filter,
772 "from": (TESTBED_ID, TUNFILTERS, "tcp"),
773 "to": (TESTBED_ID, TUNIFACE, "tcp"),
774 "init_code": functools.partial(connect_filter_peer,"tcp"),
778 "from": (TESTBED_ID, TUNFILTERS, "udp"),
779 "to": (TESTBED_ID, TUNIFACE, "udp"),
780 "init_code": functools.partial(connect_filter_peer,"udp"),
784 "from": (TESTBED_ID, TAPIFACE, "tcp"),
785 "to": (TESTBED_ID, TAPIFACE, "tcp"),
786 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
790 "from": (TESTBED_ID, TAPIFACE, "udp"),
791 "to": (TESTBED_ID, TAPIFACE, "udp"),
792 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
796 "from": (TESTBED_ID, TAPIFACE, "gre"),
797 "to": (TESTBED_ID, TAPIFACE, "gre"),
798 "init_code": functools.partial(connect_tun_iface_peer,"gre"),
802 "from": (TESTBED_ID, TAPIFACE, "fd->"),
803 "to": (TESTBED_ID, TAPFILTERS, "->fd"),
804 "init_code": connect_tun_iface_filter,
808 "from": (TESTBED_ID, TAPFILTERS, "tcp"),
809 "to": (TESTBED_ID, TAPIFACE, "tcp"),
810 "init_code": functools.partial(connect_filter_peer,"tcp"),
814 "from": (TESTBED_ID, TAPFILTERS, "udp"),
815 "to": (TESTBED_ID, TAPIFACE, "udp"),
816 "init_code": functools.partial(connect_filter_peer,"udp"),
820 "from": (TESTBED_ID, TUNFILTERS, "tcp"),
821 "to": (TESTBED_ID, TUNFILTERS, "tcp"),
822 "init_code": functools.partial(connect_filter_filter,"tcp"),
826 "from": (TESTBED_ID, TUNFILTERS, "udp"),
827 "to": (TESTBED_ID, TUNFILTERS, "udp"),
828 "init_code": functools.partial(connect_filter_filter,"udp"),
832 "from": (TESTBED_ID, TAPFILTERS, "tcp"),
833 "to": (TESTBED_ID, TAPFILTERS, "tcp"),
834 "init_code": functools.partial(connect_filter_filter,"tcp"),
838 "from": (TESTBED_ID, TAPFILTERS, "udp"),
839 "to": (TESTBED_ID, TAPFILTERS, "udp"),
840 "init_code": functools.partial(connect_filter_filter,"udp"),
844 "from": (TESTBED_ID, TUNIFACE, "tcp"),
845 "to": (None, None, "tcp"),
846 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
847 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
851 "from": (TESTBED_ID, TUNIFACE, "udp"),
852 "to": (None, None, "udp"),
853 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
854 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
858 "from": (TESTBED_ID, TUNIFACE, "fd->"),
859 "to": (None, None, "->fd"),
860 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
864 "from": (TESTBED_ID, TUNIFACE, "gre"),
865 "to": (None, None, "gre"),
866 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
870 "from": (TESTBED_ID, TAPIFACE, "tcp"),
871 "to": (None, None, "tcp"),
872 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
873 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
877 "from": (TESTBED_ID, TAPIFACE, "udp"),
878 "to": (None, None, "udp"),
879 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
880 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
884 "from": (TESTBED_ID, TAPIFACE, "fd->"),
885 "to": (None, None, "->fd"),
886 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
889 # EGRE is an extension of PlanetLab, so we can't connect externally
890 # if the other testbed isn't another PlanetLab
892 "from": (TESTBED_ID, TAPIFACE, "gre"),
893 "to": (TESTBED_ID, None, "gre"),
894 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
898 "from": (TESTBED_ID, ALLFILTERS, "tcp"),
899 "to": (None, None, "tcp"),
900 "init_code": functools.partial(crossconnect_filter_peer_init,"tcp"),
901 "compl_code": functools.partial(crossconnect_filter_peer_compl,"tcp"),
905 "from": (TESTBED_ID, ALLFILTERS, "udp"),
906 "to": (None, None, "udp"),
907 "init_code": functools.partial(crossconnect_filter_peer_init,"udp"),
908 "compl_code": functools.partial(crossconnect_filter_peer_compl,"udp"),
914 "forward_X11": dict({
915 "name": "forward_X11",
916 "help": "Forward x11 from main namespace to the node",
917 "type": Attribute.BOOL,
919 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
920 "validation_function": validation.is_bool,
924 "help": "Constrain hostname during resource discovery. May use wildcards.",
925 "type": Attribute.STRING,
926 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
927 "validation_function": validation.is_string,
931 "help": "Constrain location (city) during resource discovery. May use wildcards.",
932 "type": Attribute.STRING,
933 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
934 "validation_function": validation.is_string,
938 "help": "Constrain location (country) during resource discovery. May use wildcards.",
939 "type": Attribute.STRING,
940 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
941 "validation_function": validation.is_string,
945 "help": "Constrain location (region) during resource discovery. May use wildcards.",
946 "type": Attribute.STRING,
947 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
948 "validation_function": validation.is_string,
950 "architecture": dict({
951 "name": "architecture",
952 "help": "Constrain architexture during resource discovery.",
953 "type": Attribute.ENUM,
954 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
955 "allowed": ["x86_64",
957 "validation_function": validation.is_enum,
959 "operating_system": dict({
960 "name": "operatingSystem",
961 "help": "Constrain operating system during resource discovery.",
962 "type": Attribute.ENUM,
963 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
969 "validation_function": validation.is_enum,
973 "help": "Constrain the PlanetLab site this node should reside on.",
974 "type": Attribute.ENUM,
975 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
979 "validation_function": validation.is_enum,
981 "min_reliability": dict({
982 "name": "minReliability",
983 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
984 "type": Attribute.DOUBLE,
986 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
987 "validation_function": validation.is_number,
989 "max_reliability": dict({
990 "name": "maxReliability",
991 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
992 "type": Attribute.DOUBLE,
994 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
995 "validation_function": validation.is_number,
997 "min_bandwidth": dict({
998 "name": "minBandwidth",
999 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
1000 "type": Attribute.DOUBLE,
1002 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1003 "validation_function": validation.is_number,
1005 "max_bandwidth": dict({
1006 "name": "maxBandwidth",
1007 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1008 "type": Attribute.DOUBLE,
1010 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1011 "validation_function": validation.is_number,
1015 "help": "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
1016 "type": Attribute.DOUBLE,
1018 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1019 "validation_function": validation.is_number,
1023 "help": "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1024 "type": Attribute.DOUBLE,
1026 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1027 "validation_function": validation.is_number,
1031 "help": "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
1032 "type": Attribute.DOUBLE,
1034 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1035 "validation_function": validation.is_number,
1039 "help": "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
1040 "type": Attribute.DOUBLE,
1042 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1043 "validation_function": validation.is_number,
1049 "type": Attribute.BOOL,
1051 "validation_function": validation.is_bool
1055 "help": "This is the primary interface for the attached node",
1056 "type": Attribute.BOOL,
1058 "validation_function": validation.is_bool
1062 "help": "Device name",
1063 "type": Attribute.STRING,
1064 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1065 "validation_function": validation.is_string
1069 "help": "Maximum transmition unit for device",
1070 "type": Attribute.INTEGER,
1072 "validation_function": validation.is_integer_range(0,1500)
1076 "help": "Network mask for the device (eg: 24 for /24 network)",
1077 "type": Attribute.INTEGER,
1078 "validation_function": validation.is_integer_range(8,24)
1082 "help": "Enable SNAT (source NAT to the internet) no this device",
1083 "type": Attribute.BOOL,
1085 "validation_function": validation.is_bool
1088 "name": "multicast",
1089 "help": "Enable multicast forwarding on this device. "
1090 "Note that you still need a multicast routing daemon "
1092 "type": Attribute.BOOL,
1094 "validation_function": validation.is_bool
1096 "pointopoint": dict({
1097 "name": "pointopoint",
1098 "help": "If the interface is a P2P link, the remote endpoint's IP "
1099 "should be set on this attribute.",
1100 "type": Attribute.STRING,
1101 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1102 "validation_function": validation.is_string
1106 "help": "Emulated transmission speed (in kbytes per second)",
1107 "type": Attribute.INTEGER,
1108 "range" : (1,10*2**20),
1109 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1110 "validation_function": validation.is_integer
1112 "txqueuelen": dict({
1113 "name": "txqueuelen",
1114 "help": "Transmission queue length (in packets)",
1115 "type": Attribute.INTEGER,
1117 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1118 "range" : (1,10000),
1119 "validation_function": validation.is_integer
1124 "help": "Command line string",
1125 "type": Attribute.STRING,
1126 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1127 "validation_function": validation.is_string
1130 "name": "ccnroutes",
1131 "help": "Route can be static (e.g. udp ip) or multicast (e.g. udp 224.0.0.204 2869). To separate different route use '|' ",
1132 "type": Attribute.STRING,
1133 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1134 "validation_function": validation.is_string
1138 "help": "Run with root privileges",
1139 "type": Attribute.BOOL,
1140 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1142 "validation_function": validation.is_bool
1146 "help": "Standard input",
1147 "type": Attribute.STRING,
1148 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1149 "validation_function": validation.is_string
1154 "help": "Space-separated list of packages required to run the application",
1155 "type": Attribute.STRING,
1156 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1157 "validation_function": validation.is_string
1159 "build-depends": dict({
1160 "name": "buildDepends",
1161 "help": "Space-separated list of packages required to build the application",
1162 "type": Attribute.STRING,
1163 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1164 "validation_function": validation.is_string
1166 "rpm-fusion": dict({
1167 "name": "rpmFusion",
1168 "help": "True if required packages can be found in the RpmFusion repository",
1169 "type": Attribute.BOOL,
1170 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1172 "validation_function": validation.is_bool
1176 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
1177 "Archives won't be expanded automatically.",
1178 "type": Attribute.STRING,
1179 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1180 "validation_function": validation.is_string
1182 "ccnsources": dict({
1183 "name": "ccnsources",
1184 "help": "Path to local tar with ccnx sources."
1185 "Default source is http://www.ccnx.org/releases/ccnx-0.5.1.tar.gz",
1186 "type": Attribute.STRING,
1187 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1188 "validation_function": validation.is_string
1190 "ccnxversion": dict({
1191 "name": "ccnxversion",
1192 "help": "Version of ccnx source code to install in the node.",
1193 "type": Attribute.ENUM,
1194 "value": "ccnx-0.6.0",
1195 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1196 "allowed": ["ccnx-0.6.0",
1198 "validation_function": validation.is_enum,
1202 "help": "Build commands to execute after deploying the sources. "
1203 "Sources will be in the ${SOURCES} folder. "
1204 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
1205 "Try to make the commands return with a nonzero exit code on error.\n"
1206 "Also, do not install any programs here, use the 'install' attribute. This will "
1207 "help keep the built files constrained to the build folder (which may "
1208 "not be the home folder), and will result in faster deployment. Also, "
1209 "make sure to clean up temporary files, to reduce bandwidth usage between "
1210 "nodes when transferring built packages.",
1211 "type": Attribute.STRING,
1212 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1213 "validation_function": validation.is_string
1217 "help": "Commands to transfer built files to their final destinations. "
1218 "Sources will be in the initial working folder, and a special "
1219 "tag ${SOURCES} can be used to reference the experiment's "
1220 "home folder (where the application commands will run).\n"
1221 "ALL sources and targets needed for execution must be copied there, "
1222 "if building has been enabled.\n"
1223 "That is, 'slave' nodes will not automatically get any source files. "
1224 "'slave' nodes don't get build dependencies either, so if you need "
1225 "make and other tools to install, be sure to provide them as "
1226 "actual dependencies instead.",
1227 "type": Attribute.STRING,
1228 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1229 "validation_function": validation.is_string
1232 "netpipe_mode": dict({
1234 "help": "Link mode:\n"
1235 " * SERVER: applies to incoming connections\n"
1236 " * CLIENT: applies to outgoing connections\n"
1237 " * SERVICE: applies to both",
1238 "type": Attribute.ENUM,
1239 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1240 "allowed": ["SERVER",
1243 "validation_function": validation.is_enum,
1247 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
1248 "type": Attribute.STRING,
1249 "validation_function": is_portlist,
1253 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
1254 "type": Attribute.STRING,
1255 "validation_function": is_addrlist,
1259 "help": "Inbound bandwidth limit (in Mbit/s)",
1260 "type": Attribute.DOUBLE,
1261 "validation_function": validation.is_number,
1265 "help": "Outbound bandwidth limit (in Mbit/s)",
1266 "type": Attribute.DOUBLE,
1267 "validation_function": validation.is_number,
1271 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
1272 "type": Attribute.DOUBLE,
1273 "validation_function": validation.is_number,
1277 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
1278 "type": Attribute.DOUBLE,
1279 "validation_function": validation.is_number,
1283 "help": "Inbound packet delay (in milliseconds)",
1284 "type": Attribute.INTEGER,
1286 "validation_function": validation.is_integer,
1290 "help": "Outbound packet delay (in milliseconds)",
1291 "type": Attribute.INTEGER,
1293 "validation_function": validation.is_integer,
1297 "help": "Path to a .c or .py source for a filter module, or a binary .so",
1298 "type": Attribute.STRING,
1299 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1300 "validation_function": validation.is_string
1304 "help": "Module arguments - comma-separated list of name=value pairs",
1305 "type": Attribute.STRING,
1306 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1307 "validation_function": validation.is_string
1309 "routing_algorithm": dict({
1310 "name": "algorithm",
1311 "help": "Routing algorithm.",
1313 "type": Attribute.ENUM,
1314 "allowed": ["dvmrp"],
1315 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1316 "validation_function": validation.is_enum,
1323 "help": "Standard output stream"
1327 "help": "Application standard error",
1331 "help": "Output of the build process",
1334 "netpipe_stats": dict({
1335 "name": "netpipeStats",
1336 "help": "Information about rule match counters, packets dropped, etc.",
1341 "help": "Detailled log of all packets going through the interface",
1345 "help": "PCAP trace of all packets going through the interface",
1349 "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]#}' ",
1351 "dropped_stats": dict({
1352 "name": "dropped_stats",
1353 "help": "Information on dropped packets on a filer or queue associated to a network interface",
1358 INTERNET, NODE, NODEIFACE, CLASSQUEUEFILTER, TOSQUEUEFILTER,
1359 MULTICASTANNOUNCER, MULTICASTFORWARDER, MULTICASTROUTER,
1360 TUNFILTER, TAPIFACE, TUNIFACE, NETPIPE,
1361 NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, CCNXDAEMON, APPLICATION ]
1364 INTERNET, Parallel(NODE),
1366 Parallel(MULTICASTANNOUNCER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTROUTER),
1367 Parallel(TAPIFACE), Parallel(TUNIFACE), NETPIPE,
1368 Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(CCNXDAEMON),
1369 Parallel(APPLICATION)]
1371 # Start (and prestart) node after ifaces, because the node needs the ifaces in order to set up routes
1372 start_order = [ INTERNET,
1374 Parallel(TAPIFACE), Parallel(TUNIFACE),
1375 Parallel(NODE), NETPIPE,
1376 Parallel(MULTICASTANNOUNCER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTROUTER),
1377 Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(CCNXDAEMON),
1378 Parallel(APPLICATION)]
1382 Parallel(APPLICATION),
1383 Parallel (CCNXDAEMON),
1384 Parallel(MULTICASTROUTER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTANNOUNCER),
1385 Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NETPIPE),
1386 Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY),
1387 NODEIFACE, Parallel(NODE) ]
1389 factories_info = dict({
1391 "help": "Virtualized Node (V-Server style)",
1392 "category": FC.CATEGORY_NODES,
1393 "create_function": create_node,
1394 "preconfigure_function": configure_node,
1395 "prestart_function": configure_node_routes,
1411 # NEPI-in-NEPI attributes
1412 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
1414 "connector_types": ["devs", "apps", "pipes", "deps"],
1415 "tags": [tags.NODE, tags.ALLOW_ROUTES],
1418 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
1419 "category": FC.CATEGORY_DEVICES,
1420 "create_function": create_nodeiface,
1421 "preconfigure_function": configure_nodeiface,
1422 "box_attributes": [ ],
1423 "connector_types": ["node", "inet"],
1424 "tags": [tags.INTERFACE, tags.HAS_ADDRESSES],
1427 "help": "Virtual TUN network interface (layer 3)",
1428 "category": FC.CATEGORY_DEVICES,
1429 "create_function": create_tuniface,
1430 "preconfigure_function": preconfigure_tuniface,
1431 "configure_function": postconfigure_tuniface,
1432 "prestart_function": prestart_tuniface,
1434 "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
1436 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1438 "traces": ["packets", "pcap"],
1439 "connector_types": ["node","udp","tcp","fd->","gre"],
1440 "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
1443 "help": "Virtual TAP network interface (layer 2)",
1444 "category": FC.CATEGORY_DEVICES,
1445 "create_function": create_tapiface,
1446 "preconfigure_function": preconfigure_tuniface,
1447 "configure_function": postconfigure_tuniface,
1448 "prestart_function": prestart_tuniface,
1450 "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
1452 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1454 "traces": ["packets", "pcap"],
1455 "connector_types": ["node","udp","tcp","fd->","gre"],
1456 "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
1459 "help": "TUN/TAP stream filter\n\n"
1460 "If specified, it should be either a .py or .so module. "
1461 "It will be loaded, and all incoming and outgoing packets "
1462 "will be routed through it. The filter will not be responsible "
1463 "for buffering, packet queueing is performed in tun_connect "
1464 "already, so it should not concern itself with it. It should "
1465 "not, however, block in one direction if the other is congested.\n"
1467 "Modules are expected to have the following methods:\n"
1469 "\t\tIf arguments are given, this method will be called with the\n"
1470 "\t\tgiven arguments (as keyword args in python modules, or a single\n"
1471 "\taccept_packet(packet, direction):\n"
1472 "\t\tDecide whether to drop the packet. Direction is 0 for packets "
1473 "coming from the local side to the remote, and 1 is for packets "
1474 "coming from the remote side to the local. Return a boolean, "
1475 "true if the packet is not to be dropped.\n"
1476 "\tfilter_init():\n"
1477 "\t\tInitializes a filtering pipe (filter_run). It should "
1478 "return two file descriptors to use as a bidirectional "
1479 "pipe: local and remote. 'local' is where packets from the "
1480 "local side will be written to. After filtering, those packets "
1481 "should be written to 'remote', where tun_connect will read "
1482 "from, and it will forward them to the remote peer. "
1483 "Packets from the remote peer will be written to 'remote', "
1484 "where the filter is expected to read from, and eventually "
1485 "forward them to the local side. If the file descriptors are "
1486 "not nonblocking, they will be set to nonblocking. So it's "
1487 "better to set them from the start like that.\n"
1488 "\tfilter_run(local, remote):\n"
1489 "\t\tIf filter_init is provided, it will be called repeatedly, "
1490 "in a separate thread until the process is killed. It should "
1491 "sleep at most for a second.\n"
1492 "\tfilter_close(local, remote):\n"
1493 "\t\tCalled then the process is killed, if filter_init was provided. "
1494 "It should, among other things, close the file descriptors.\n"
1496 "Python modules are expected to return a tuple in filter_init, "
1497 "either of file descriptors or file objects, while native ones "
1498 "will receive two int*.\n"
1500 "Python modules can additionally contain a custom queue class "
1501 "that will replace the FIFO used by default. The class should "
1502 "be named 'queueclass' and contain an interface compatible with "
1503 "collections.deque. That is, indexing (especiall for q[0]), "
1504 "bool(q), popleft, appendleft, pop (right), append (right), "
1505 "len(q) and clear.",
1506 "category": FC.CATEGORY_CHANNELS,
1507 "create_function": create_tunfilter,
1510 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1512 "connector_types": ["->fd","udp","tcp"],
1514 CLASSQUEUEFILTER : dict({
1515 "help": "TUN classfull queue, uses a separate queue for each user-definable class.\n\n"
1516 "It takes two arguments, both of which have sensible defaults:\n"
1517 "\tsize: the base size of each class' queue\n"
1518 "\tclasses: the class definitions, which follow the following syntax:\n"
1519 '\t <CLASSLIST> ::= <CLASS> ":" CLASSLIST\n'
1521 '\t <CLASS> ::= <PROTOLIST> "*" <PRIORITYSPEC>\n'
1522 '\t | <DFLTCLASS>\n'
1523 '\t <DFLTCLASS> ::= "*" <PRIORITYSPEC>\n'
1524 '\t <PROTOLIST> ::= <PROTO> "." <PROTOLIST>\n'
1526 '\t <PROTO> ::= <NAME> | <NUMBER>\n'
1527 '\t <NAME> ::= --see http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers --\n'
1528 '\t --only in lowercase, with special characters removed--\n'
1529 '\t --or see below--\n'
1530 '\t <NUMBER> ::= [0-9]+\n'
1531 '\t <PRIORITYSPEC> ::= <THOUGHPUT> [ "#" <SIZE> ] [ "p" <PRIORITY> ]\n'
1532 '\t <THOUGHPUT> ::= NUMBER -- default 1\n'
1533 '\t <PRIORITY> ::= NUMBER -- default 0\n'
1534 '\t <SIZE> ::= NUMBER -- default 1\n'
1536 "Size, thoughput and priority are all relative terms. "
1537 "Sizes are multipliers for the size argument, thoughput "
1538 "is applied relative to other classes and the same with "
1540 "category": FC.CATEGORY_CHANNELS,
1541 "create_function": create_classqueuefilter,
1544 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1546 "connector_types": ["->fd","udp","tcp"],
1547 "traces": ["dropped_stats"],
1549 TOSQUEUEFILTER : dict({
1550 "help": "TUN classfull queue that classifies according to the TOS (RFC 791) IP field.\n\n"
1551 "It takes a size argument that specifies the size of each class. As TOS is a "
1552 "subset of DiffServ, this queue half-implements DiffServ.",
1553 "category": FC.CATEGORY_CHANNELS,
1554 "create_function": create_tosqueuefilter,
1557 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1559 "connector_types": ["->fd","udp","tcp"],
1563 "help": "Generic executable command line application",
1564 "category": FC.CATEGORY_APPLICATIONS,
1565 "create_function": create_application,
1566 "start_function": start_application,
1567 "status_function": status_application,
1568 "stop_function": stop_application,
1569 "configure_function": configure_application,
1570 "box_attributes": ["command", "sudo", "stdin",
1571 "depends", "build-depends", "build", "install",
1572 "sources", "rpm-fusion" ],
1573 "connector_types": ["node"],
1574 "traces": ["stdout", "stderr", "buildlog", "output"],
1575 "tags": [tags.APPLICATION],
1579 "help": "CCNx daemon",
1580 "category": FC.CATEGORY_APPLICATIONS,
1581 "create_function": create_ccnxdaemon,
1582 "prestart_function": prestart_ccnxdaemon,
1583 "status_function": status_application,
1584 "stop_function": stop_application,
1585 "configure_function": configure_application,
1586 "box_attributes": ["ccnroutes", "ccnsources", "build",
1587 "install", "ccnxversion", "sources"],
1588 "connector_types": ["node"],
1589 "traces": ["stdout", "stderr", "buildlog", "output"],
1590 "tags": [tags.APPLICATION],
1593 "help": "Requirement for package or application to be installed on some node",
1594 "category": FC.CATEGORY_APPLICATIONS,
1595 "create_function": create_dependency,
1596 "preconfigure_function": configure_dependency,
1597 "box_attributes": ["depends", "build-depends", "build", "install",
1598 "sources", "rpm-fusion" ],
1599 "connector_types": ["node"],
1600 "traces": ["buildlog"],
1602 NEPIDEPENDENCY: dict({
1603 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
1604 "category": FC.CATEGORY_APPLICATIONS,
1605 "create_function": create_nepi_dependency,
1606 "preconfigure_function": configure_dependency,
1607 "box_attributes": [],
1608 "connector_types": ["node"],
1609 "traces": ["buildlog"],
1611 NS3DEPENDENCY: dict({
1612 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
1613 "category": FC.CATEGORY_APPLICATIONS,
1614 "create_function": create_ns3_dependency,
1615 "preconfigure_function": configure_dependency,
1616 "box_attributes": [ ],
1617 "connector_types": ["node"],
1618 "traces": ["buildlog"],
1620 MULTICASTFORWARDER: dict({
1621 "help": "This application installs a userspace packet forwarder "
1622 "that, when connected to a node, filters all packets "
1623 "flowing through multicast-capable virtual interfaces "
1624 "and applies custom-specified routing policies.",
1625 "category": FC.CATEGORY_APPLICATIONS,
1626 "create_function": create_multicast_forwarder,
1627 "preconfigure_function": configure_forwarder,
1628 "start_function": start_application,
1629 "status_function": status_application,
1630 "stop_function": stop_application,
1631 "box_attributes": [ ],
1632 "connector_types": ["node","router"],
1633 "traces": ["buildlog","stderr"],
1635 MULTICASTANNOUNCER: dict({
1636 "help": "This application installs a userspace daemon that "
1637 "monitors multicast membership and announces it on all "
1638 "multicast-capable interfaces.\n"
1639 "This does not usually happen automatically on PlanetLab slivers.",
1640 "category": FC.CATEGORY_APPLICATIONS,
1641 "create_function": create_multicast_announcer,
1642 "preconfigure_function": configure_announcer,
1643 "start_function": start_application,
1644 "status_function": status_application,
1645 "stop_function": stop_application,
1646 "box_attributes": [ ],
1647 "connector_types": ["node"],
1648 "traces": ["buildlog","stderr"],
1650 MULTICASTROUTER: dict({
1651 "help": "This application installs a userspace daemon that "
1652 "monitors multicast membership and announces it on all "
1653 "multicast-capable interfaces.\n"
1654 "This does not usually happen automatically on PlanetLab slivers.",
1655 "category": FC.CATEGORY_APPLICATIONS,
1656 "create_function": create_multicast_router,
1657 "preconfigure_function": configure_router,
1658 "start_function": start_application,
1659 "status_function": status_application,
1660 "stop_function": stop_application,
1661 "box_attributes": ["routing_algorithm"],
1662 "connector_types": ["fwd"],
1663 "traces": ["buildlog","stdout","stderr"],
1666 "help": "Internet routing",
1667 "category": FC.CATEGORY_CHANNELS,
1668 "create_function": create_internet,
1669 "connector_types": ["devs"],
1670 "tags": [tags.INTERNET],
1673 "help": "Link emulation",
1674 "category": FC.CATEGORY_CHANNELS,
1675 "create_function": create_netpipe,
1676 "configure_function": configure_netpipe,
1677 "box_attributes": ["netpipe_mode",
1678 "addr_list", "port_list",
1679 "bw_in","plr_in","delay_in",
1680 "bw_out","plr_out","delay_out"],
1681 "connector_types": ["node"],
1682 "traces": ["netpipe_stats"],
1686 testbed_attributes = dict({
1689 "help": "The hierarchical Resource Name (HRN) for the PlanetLab slice.",
1690 "type": Attribute.STRING,
1691 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1692 "validation_function": validation.is_string
1696 "help": "Activates the use of SFA for node reservation.",
1697 "type": Attribute.BOOL,
1698 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1699 "validation_function": validation.is_bool
1703 "help": "The name of the PlanetLab slice to use",
1704 "type": Attribute.STRING,
1705 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1706 "validation_function": validation.is_string
1710 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
1711 "type": Attribute.STRING,
1712 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1713 "validation_function": validation.is_string
1717 "help": "The PlanetLab user's password.",
1718 "type": Attribute.STRING,
1719 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1720 "validation_function": validation.is_string
1724 "help": "The PlanetLab PLC API host",
1725 "type": Attribute.STRING,
1726 "value": "www.planet-lab.eu",
1727 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1728 "validation_function": validation.is_string
1732 "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.",
1733 "type": Attribute.STRING,
1734 "value": "https://%(hostname)s:443/PLCAPI/",
1735 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1736 "validation_function": validation.is_string
1738 "p2p_deployment": dict({
1739 "name": "p2pDeployment",
1740 "help": "Enable peer-to-peer deployment of applications and dependencies. "
1741 "When enabled, dependency packages and applications are "
1742 "deployed in a P2P fashion, picking a single node to do "
1743 "the building or repo download, while all the others "
1744 "cooperatively exchange resulting binaries or rpms. "
1745 "When deploying to many nodes, this is a far more efficient "
1746 "use of resources. It does require re-encrypting and distributing "
1747 "the slice's private key. Though it is implemented in a secure "
1748 "fashion, if they key's sole purpose is not PlanetLab, then this "
1749 "feature should be disabled.",
1750 "type": Attribute.BOOL,
1752 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1753 "validation_function": validation.is_bool
1755 "slice_ssh_key": dict({
1756 "name": "sliceSSHKey",
1757 "help": "The controller-local path to the slice user's ssh private key. "
1758 "It is the user's responsability to deploy this file where the controller "
1759 "will run, it won't be done automatically because it's sensitive information. "
1760 "It is recommended that a NEPI-specific user be created for this purpose and "
1761 "this purpose alone.",
1762 "type": Attribute.STRING,
1763 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1764 "validation_function": validation.is_string
1766 "pl_log_level": dict({
1767 "name": "plLogLevel",
1768 "help": "Verbosity of logging of planetlab events.",
1770 "type": Attribute.ENUM,
1771 "allowed": ["DEBUG",
1776 "validation_function": validation.is_enum,
1778 "tap_port_base": dict({
1779 "name": "tapPortBase",
1780 "help": "Base port to use when connecting TUN/TAPs. Effective port will be BASE + GUID.",
1781 "type": Attribute.INTEGER,
1783 "range": (2000,30000),
1784 "validation_function": validation.is_integer_range(2000,30000)
1786 "clean_proc": dict({
1787 "name": "cleanProc",
1788 "help": "Set to True if the slice will be dedicated to this experiment. "
1789 "NEPI will perform node and slice process cleanup, making sure slices are "
1790 "in a clean, repeatable state before running the experiment.",
1791 "type": Attribute.BOOL,
1793 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1794 "validation_function": validation.is_bool
1796 "clean_home": dict({
1797 "name": "cleanHome",
1798 "help": "Set to True all preexistent directories in the home "
1799 "directory of each sliver will be removed before the "
1800 "start of the experiment.",
1801 "type": Attribute.BOOL,
1803 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1804 "validation_function": validation.is_bool
1808 supported_recovery_policies = [
1814 class MetadataInfo(metadata.MetadataInfo):
1816 def connector_types(self):
1817 return connector_types
1820 def connections(self):
1824 def attributes(self):
1832 def create_order(self):
1836 def configure_order(self):
1837 return configure_order
1840 def prestart_order(self):
1844 def start_order(self):
1848 def factories_info(self):
1849 return factories_info
1852 def testbed_attributes(self):
1853 return testbed_attributes
1856 def testbed_id(self):
1860 def testbed_version(self):
1861 return TESTBED_VERSION
1864 def supported_recovery_policies(self):
1865 return supported_recovery_policies