2 # -*- coding: utf-8 -*-
6 from constants import TESTBED_ID, TESTBED_VERSION
7 from nepi.core import metadata
8 from nepi.core.metadata import Parallel
9 from nepi.core.attributes import Attribute
10 from nepi.util import tags, validation
11 from nepi.util.constants import ApplicationStatus as AS, \
12 FactoryCategories as FC, \
13 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP, \
14 DeploymentConfiguration as DC
22 NODEIFACE = "NodeInterface"
23 TUNIFACE = "TunInterface"
24 TAPIFACE = "TapInterface"
25 APPLICATION = "Application"
26 DEPENDENCY = "Dependency"
27 NEPIDEPENDENCY = "NepiDependency"
28 NS3DEPENDENCY = "NS3Dependency"
31 TUNFILTER = "TunFilter"
32 CLASSQUEUEFILTER = "ClassQueueFilter"
33 TOSQUEUEFILTER = "TosQueueFilter"
35 TUNFILTERS = (TUNFILTER, CLASSQUEUEFILTER, TOSQUEUEFILTER)
36 TAPFILTERS = (TUNFILTER, )
37 ALLFILTERS = (TUNFILTER, CLASSQUEUEFILTER, TOSQUEUEFILTER)
39 PL_TESTBED_ID = "planetlab"
42 ### Custom validation functions ###
43 def is_addrlist(attribute, value):
44 if not validation.is_string(attribute, value):
51 components = value.split(',')
53 for component in components:
55 addr, mask = component.split('/',1)
57 addr, mask = component, '32'
59 if mask is not None and not (mask and mask.isdigit()):
60 # No empty or nonnumeric masks
63 if not validation.is_ip4_address(attribute, addr):
64 # Address part must be ipv4
69 def is_portlist(attribute, value):
70 if not validation.is_string(attribute, value):
77 components = value.split(',')
79 for component in components:
81 pfrom, pto = component.split('-',1)
83 pfrom = pto = component
85 if not pfrom or not pto or not pfrom.isdigit() or not pto.isdigit():
86 # No empty or nonnumeric ports
92 ### Connection functions ####
94 def connect_node_iface_node(testbed_instance, node_guid, iface_guid):
95 node = testbed_instance._elements[node_guid]
96 iface = testbed_instance._elements[iface_guid]
99 def connect_node_iface_inet(testbed_instance, iface_guid, inet_guid):
100 iface = testbed_instance._elements[iface_guid]
101 iface.has_internet = True
103 def connect_tun_iface_node(testbed_instance, node_guid, iface_guid):
104 node = testbed_instance._elements[node_guid]
105 iface = testbed_instance._elements[iface_guid]
107 node.required_vsys.update(('fd_tuntap', 'vif_up', 'vif_down'))
108 node.required_packages.update(('python', 'python-crypto', 'python-setuptools', 'gcc'))
110 def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid):
111 iface = testbed_instance._elements[iface_guid]
112 peer_iface = testbed_instance._elements[peer_iface_guid]
113 iface.peer_iface = peer_iface
114 peer_iface.peer_iface = iface
117 peer_iface.peer_proto = \
118 peer_iface.tun_proto = proto
119 iface.tun_key = peer_iface.tun_key
121 def connect_tun_iface_filter(testbed_instance, iface_guid, filter_guid):
122 iface = testbed_instance._elements[iface_guid]
123 filt = testbed_instance._elements[filter_guid]
124 traces = testbed_instance._get_traces(filter_guid)
125 if 'dropped_stats' in traces:
126 args = filt.args if filt.args else ""
127 filt.args = ','.join(filt.args.split(',') + ["logdropped=true",])
128 iface.filter_module = filt
129 filt.iface_guid = iface_guid
130 filt.iface = weakref.ref(iface)
133 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
135 def connect_filter_peer(proto, testbed_instance, filter_guid, peer_guid):
136 peer = testbed_instance._elements[peer_guid]
137 filt = testbed_instance._elements[filter_guid]
138 filt.peer_proto = proto
139 filt.peer_guid = peer_guid
141 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
143 def connect_filter_filter(proto, testbed_instance, filter_guid, peer_guid):
144 peer = testbed_instance._elements[peer_guid]
145 filt = testbed_instance._elements[filter_guid]
146 filt.peer_proto = proto
147 peer.peer_proto = proto
149 peer.peer_guid = filt.iface_guid
151 filt.peer_guid = peer.iface_guid
152 if filt.iface_guid and filt.peer_guid:
153 connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
155 def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data):
156 iface = testbed_instance._elements[iface_guid]
157 iface.peer_iface = None
158 iface.peer_addr = peer_iface_data.get("tun_addr")
159 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
160 iface.peer_port = peer_iface_data.get("tun_port")
161 iface.peer_cipher = peer_iface_data.get("tun_cipher")
162 iface.tun_key = min(iface.tun_key, peer_iface_data.get("tun_key"))
163 iface.tun_proto = proto
165 preconfigure_tuniface(testbed_instance, iface_guid)
167 def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data):
168 # refresh (refreshable) attributes for second-phase
169 iface = testbed_instance._elements[iface_guid]
170 iface.peer_addr = peer_iface_data.get("tun_addr")
171 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
172 iface.peer_port = peer_iface_data.get("tun_port")
173 iface.peer_cipher = peer_iface_data.get("tun_cipher")
175 postconfigure_tuniface(testbed_instance, iface_guid)
177 def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_iface_data):
178 crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
179 crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
181 def crossconnect_filter_peer_init(proto, testbed_instance, filter_guid, peer_data):
182 filt = testbed_instance._elements[filter_guid]
183 filt.peer_proto = proto
184 crossconnect_tun_iface_peer_init(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
186 def crossconnect_filter_peer_compl(proto, testbed_instance, filter_guid, peer_data):
187 filt = testbed_instance._elements[filter_guid]
188 filt.peer_proto = proto
189 crossconnect_tun_iface_peer_compl(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
191 def crossconnect_filter_peer_both(proto, testbed_instance, filter_guid, peer_data):
192 crossconnect_filter_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
193 crossconnect_filter_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
195 def connect_dep(testbed_instance, node_guid, app_guid):
196 node = testbed_instance._elements[node_guid]
197 app = testbed_instance._elements[app_guid]
201 node.required_packages.update(set(
202 app.depends.split() ))
205 if app.home_path and app.home_path not in node.pythonpath:
206 node.pythonpath.append(app.home_path)
209 for envkey, envval in app.env.iteritems():
210 envval = app._replace_paths(envval)
211 node.env[envkey].append(envval)
214 node.rpmFusion = True
216 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
217 node = testbed_instance._elements[node_guid]
218 netpipe = testbed_instance._elements[netpipe_guid]
220 node.required_vsys.add('ipfw-be')
221 node.required_packages.add('ipfwslice')
224 ### Creation functions ###
226 def create_node(testbed_instance, guid):
227 parameters = testbed_instance._get_parameters(guid)
229 # create element with basic attributes
230 element = testbed_instance._make_node(parameters)
232 # add constraint on number of (real) interfaces
233 # by counting connected devices
234 dev_guids = testbed_instance.get_connected(guid, "devs", "node")
235 num_open_ifaces = sum( # count True values
236 NODEIFACE == testbed_instance._get_factory_id(guid)
237 for guid in dev_guids )
238 element.min_num_external_ifaces = num_open_ifaces
240 # require vroute vsys if we have routes to set up
241 routes = testbed_instance._add_route.get(guid)
243 vsys = element.routing_method(routes,
244 testbed_instance.vsys_vnet)
245 element.required_vsys.add(vsys)
247 testbed_instance.elements[guid] = element
249 def create_nodeiface(testbed_instance, guid):
250 parameters = testbed_instance._get_parameters(guid)
251 element = testbed_instance._make_node_iface(parameters)
252 testbed_instance.elements[guid] = element
254 def create_tuniface(testbed_instance, guid):
255 parameters = testbed_instance._get_parameters(guid)
256 element = testbed_instance._make_tun_iface(parameters)
258 # Set custom addresses, if there are any already
259 # Setting this early helps set up P2P links
260 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
261 addresses = testbed_instance._add_address[guid]
262 for address in addresses:
263 (address, netprefix, broadcast) = address
264 element.add_address(address, netprefix, broadcast)
266 testbed_instance.elements[guid] = element
268 def create_tapiface(testbed_instance, guid):
269 parameters = testbed_instance._get_parameters(guid)
270 element = testbed_instance._make_tap_iface(parameters)
272 # Set custom addresses, if there are any already
273 # Setting this early helps set up P2P links
274 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
275 addresses = testbed_instance._add_address[guid]
276 for address in addresses:
277 (address, netprefix, broadcast) = address
278 element.add_address(address, netprefix, broadcast)
280 testbed_instance.elements[guid] = element
282 def create_tunfilter(testbed_instance, guid):
283 parameters = testbed_instance._get_parameters(guid)
284 element = testbed_instance._make_tun_filter(parameters)
285 testbed_instance.elements[guid] = element
287 def create_classqueuefilter(testbed_instance, guid):
288 parameters = testbed_instance._get_parameters(guid)
289 element = testbed_instance._make_class_queue_filter(parameters)
290 testbed_instance.elements[guid] = element
292 def create_tosqueuefilter(testbed_instance, guid):
293 parameters = testbed_instance._get_parameters(guid)
294 element = testbed_instance._make_tos_queue_filter(parameters)
295 testbed_instance.elements[guid] = element
297 def create_application(testbed_instance, guid):
298 parameters = testbed_instance._get_parameters(guid)
299 element = testbed_instance._make_application(parameters)
301 # Just inject configuration stuff
302 element.home_path = "nepi-app-%s" % (guid,)
304 testbed_instance.elements[guid] = element
306 def create_dependency(testbed_instance, guid):
307 parameters = testbed_instance._get_parameters(guid)
308 element = testbed_instance._make_dependency(parameters)
310 # Just inject configuration stuff
311 element.home_path = "nepi-dep-%s" % (guid,)
313 testbed_instance.elements[guid] = element
315 def create_nepi_dependency(testbed_instance, guid):
316 parameters = testbed_instance._get_parameters(guid)
317 element = testbed_instance._make_nepi_dependency(parameters)
319 # Just inject configuration stuff
320 element.home_path = "nepi-nepi-%s" % (guid,)
322 testbed_instance.elements[guid] = element
324 def create_ns3_dependency(testbed_instance, guid):
325 parameters = testbed_instance._get_parameters(guid)
326 element = testbed_instance._make_ns3_dependency(parameters)
328 # Just inject configuration stuff
329 element.home_path = "nepi-ns3-%s" % (guid,)
331 testbed_instance.elements[guid] = element
333 def create_internet(testbed_instance, guid):
334 parameters = testbed_instance._get_parameters(guid)
335 element = testbed_instance._make_internet(parameters)
336 testbed_instance.elements[guid] = element
338 def create_netpipe(testbed_instance, guid):
339 parameters = testbed_instance._get_parameters(guid)
340 element = testbed_instance._make_netpipe(parameters)
341 testbed_instance.elements[guid] = element
343 ### Start/Stop functions ###
345 def start_application(testbed_instance, guid):
346 parameters = testbed_instance._get_parameters(guid)
347 traces = testbed_instance._get_traces(guid)
348 app = testbed_instance.elements[guid]
350 app.stdout = "stdout" in traces
351 app.stderr = "stderr" in traces
352 app.buildlog = "buildlog" in traces
353 app.outout = "output" in traces
357 def stop_application(testbed_instance, guid):
358 app = testbed_instance.elements[guid]
361 ### Status functions ###
363 def status_application(testbed_instance, guid):
364 if guid not in testbed_instance.elements.keys():
365 return AS.STATUS_NOT_STARTED
367 app = testbed_instance.elements[guid]
370 ### Configure functions ###
372 def configure_nodeiface(testbed_instance, guid):
373 element = testbed_instance._elements[guid]
375 # Cannot explicitly configure addresses
376 if guid in testbed_instance._add_address:
377 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
380 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
381 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
382 siblings = [ self._element[dev_guid]
383 for dev_guid in dev_guids
384 if dev_guid != guid ]
386 # Fetch address from PLC api
387 element.pick_iface(siblings)
389 # Do some validations
392 def preconfigure_tuniface(testbed_instance, guid):
393 element = testbed_instance._elements[guid]
395 # Set custom addresses if any, and if not set already
396 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
397 addresses = testbed_instance._add_address[guid]
398 for address in addresses:
399 (address, netprefix, broadcast) = address
400 element.add_address(address, netprefix, broadcast)
402 # Link to external interface, if any
403 for iface in testbed_instance._elements.itervalues():
404 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
405 element.external_iface = iface
408 # Set standard TUN attributes
409 if (not element.tun_addr or not element.tun_port) and element.external_iface:
410 element.tun_addr = element.external_iface.address
411 element.tun_port = testbed_instance.tapPortBase + int(guid)
414 traces = testbed_instance._get_traces(guid)
415 for capmode in ('pcap', 'packets'):
416 if capmode in traces:
417 element.capture = capmode
420 element.capture = False
422 # Do some validations
426 if element.peer_proto:
427 if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface):
429 listening = id(element) < id(element.peer_iface)
432 if not element.tun_addr or not element.tun_port:
434 elif not element.peer_addr or not element.peer_port:
437 # both have addresses...
438 # ...the one with the lesser address listens
439 listening = element.tun_addr < element.peer_addr
444 def postconfigure_tuniface(testbed_instance, guid):
445 element = testbed_instance._elements[guid]
450 def wait_tuniface(testbed_instance, guid):
451 element = testbed_instance._elements[guid]
454 element.async_launch_wait()
457 def configure_node(testbed_instance, guid):
458 node = testbed_instance._elements[guid]
460 # Just inject configuration stuff
461 node.home_path = "nepi-node-%s" % (guid,)
462 node.ident_path = testbed_instance.sliceSSHKey
463 node.slicename = testbed_instance.slicename
465 # Do some validations
468 # this will be done in parallel in all nodes
469 # this call only spawns the process
470 node.install_dependencies()
472 def configure_node_routes(testbed_instance, guid):
473 node = testbed_instance._elements[guid]
474 routes = testbed_instance._add_route.get(guid)
478 for dev_guid in testbed_instance.get_connected(guid, "devs", "node")
479 for dev in ( testbed_instance._elements.get(dev_guid) ,)
480 if dev and isinstance(dev, testbed_instance._interfaces.TunIface) ]
482 vsys = testbed_instance.vsys_vnet
484 node.configure_routes(routes, devs, vsys)
486 def configure_application(testbed_instance, guid):
487 app = testbed_instance._elements[guid]
489 # Do some validations
492 # Wait for dependencies
493 app.node.wait_dependencies()
498 def configure_dependency(testbed_instance, guid):
499 dep = testbed_instance._elements[guid]
501 # Do some validations
504 # Wait for dependencies
505 dep.node.wait_dependencies()
510 def configure_netpipe(testbed_instance, guid):
511 netpipe = testbed_instance._elements[guid]
513 # Do some validations
516 # Wait for dependencies
517 netpipe.node.wait_dependencies()
522 ### Factory information ###
524 connector_types = dict({
526 "help": "Connector from node to applications",
532 "help": "Connector from node to network interfaces",
538 "help": "Connector from node to application dependencies "
539 "(packages and applications that need to be installed)",
545 "help": "Connector from network interfaces to the internet",
551 "help": "Connector to a Node",
557 "help": "Connector to a NetPipe",
564 "help": "ip-ip tunneling over TCP link",
570 "help": "ip-ip tunneling over UDP datagrams",
576 "help": "IP or Ethernet tunneling using the GRE protocol",
582 "help": "TUN device file descriptor provider",
588 "help": "TUN device file descriptor slot",
597 "from": (TESTBED_ID, NODE, "devs"),
598 "to": (TESTBED_ID, NODEIFACE, "node"),
599 "init_code": connect_node_iface_node,
603 "from": (TESTBED_ID, NODE, "devs"),
604 "to": (TESTBED_ID, TUNIFACE, "node"),
605 "init_code": connect_tun_iface_node,
609 "from": (TESTBED_ID, NODE, "devs"),
610 "to": (TESTBED_ID, TAPIFACE, "node"),
611 "init_code": connect_tun_iface_node,
615 "from": (TESTBED_ID, NODEIFACE, "inet"),
616 "to": (TESTBED_ID, INTERNET, "devs"),
617 "init_code": connect_node_iface_inet,
621 "from": (TESTBED_ID, NODE, "apps"),
622 "to": (TESTBED_ID, APPLICATION, "node"),
623 "init_code": connect_dep,
627 "from": (TESTBED_ID, NODE, "deps"),
628 "to": (TESTBED_ID, DEPENDENCY, "node"),
629 "init_code": connect_dep,
633 "from": (TESTBED_ID, NODE, "deps"),
634 "to": (TESTBED_ID, NEPIDEPENDENCY, "node"),
635 "init_code": connect_dep,
639 "from": (TESTBED_ID, NODE, "deps"),
640 "to": (TESTBED_ID, NS3DEPENDENCY, "node"),
641 "init_code": connect_dep,
645 "from": (TESTBED_ID, NODE, "pipes"),
646 "to": (TESTBED_ID, NETPIPE, "node"),
647 "init_code": connect_node_netpipe,
651 "from": (TESTBED_ID, TUNIFACE, "tcp"),
652 "to": (TESTBED_ID, TUNIFACE, "tcp"),
653 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
657 "from": (TESTBED_ID, TUNIFACE, "udp"),
658 "to": (TESTBED_ID, TUNIFACE, "udp"),
659 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
663 "from": (TESTBED_ID, TUNIFACE, "gre"),
664 "to": (TESTBED_ID, TUNIFACE, "gre"),
665 "init_code": functools.partial(connect_tun_iface_peer,"gre"),
669 "from": (TESTBED_ID, TUNIFACE, "fd->"),
670 "to": (TESTBED_ID, TUNFILTERS, "->fd"),
671 "init_code": connect_tun_iface_filter,
675 "from": (TESTBED_ID, TUNFILTERS, "tcp"),
676 "to": (TESTBED_ID, TUNIFACE, "tcp"),
677 "init_code": functools.partial(connect_filter_peer,"tcp"),
681 "from": (TESTBED_ID, TUNFILTERS, "udp"),
682 "to": (TESTBED_ID, TUNIFACE, "udp"),
683 "init_code": functools.partial(connect_filter_peer,"udp"),
687 "from": (TESTBED_ID, TAPIFACE, "tcp"),
688 "to": (TESTBED_ID, TAPIFACE, "tcp"),
689 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
693 "from": (TESTBED_ID, TAPIFACE, "udp"),
694 "to": (TESTBED_ID, TAPIFACE, "udp"),
695 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
699 "from": (TESTBED_ID, TAPIFACE, "gre"),
700 "to": (TESTBED_ID, TAPIFACE, "gre"),
701 "init_code": functools.partial(connect_tun_iface_peer,"gre"),
705 "from": (TESTBED_ID, TAPIFACE, "fd->"),
706 "to": (TESTBED_ID, TAPFILTERS, "->fd"),
707 "init_code": connect_tun_iface_filter,
711 "from": (TESTBED_ID, TAPFILTERS, "tcp"),
712 "to": (TESTBED_ID, TAPIFACE, "tcp"),
713 "init_code": functools.partial(connect_filter_peer,"tcp"),
717 "from": (TESTBED_ID, TAPFILTERS, "udp"),
718 "to": (TESTBED_ID, TAPIFACE, "udp"),
719 "init_code": functools.partial(connect_filter_peer,"udp"),
723 "from": (TESTBED_ID, TUNFILTERS, "tcp"),
724 "to": (TESTBED_ID, TUNFILTERS, "tcp"),
725 "init_code": functools.partial(connect_filter_filter,"tcp"),
729 "from": (TESTBED_ID, TUNFILTERS, "udp"),
730 "to": (TESTBED_ID, TUNFILTERS, "udp"),
731 "init_code": functools.partial(connect_filter_filter,"udp"),
735 "from": (TESTBED_ID, TAPFILTERS, "tcp"),
736 "to": (TESTBED_ID, TAPFILTERS, "tcp"),
737 "init_code": functools.partial(connect_filter_filter,"tcp"),
741 "from": (TESTBED_ID, TAPFILTERS, "udp"),
742 "to": (TESTBED_ID, TAPFILTERS, "udp"),
743 "init_code": functools.partial(connect_filter_filter,"udp"),
747 "from": (TESTBED_ID, TUNIFACE, "tcp"),
748 "to": (None, None, "tcp"),
749 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
750 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
754 "from": (TESTBED_ID, TUNIFACE, "udp"),
755 "to": (None, None, "udp"),
756 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
757 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
761 "from": (TESTBED_ID, TUNIFACE, "fd->"),
762 "to": (None, None, "->fd"),
763 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
767 "from": (TESTBED_ID, TUNIFACE, "gre"),
768 "to": (None, None, "gre"),
769 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
773 "from": (TESTBED_ID, TAPIFACE, "tcp"),
774 "to": (None, None, "tcp"),
775 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
776 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
780 "from": (TESTBED_ID, TAPIFACE, "udp"),
781 "to": (None, None, "udp"),
782 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
783 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
787 "from": (TESTBED_ID, TAPIFACE, "fd->"),
788 "to": (None, None, "->fd"),
789 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
792 # EGRE is an extension of PlanetLab, so we can't connect externally
793 # if the other testbed isn't another PlanetLab
795 "from": (TESTBED_ID, TAPIFACE, "gre"),
796 "to": (TESTBED_ID, None, "gre"),
797 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
801 "from": (TESTBED_ID, ALLFILTERS, "tcp"),
802 "to": (None, None, "tcp"),
803 "init_code": functools.partial(crossconnect_filter_peer_init,"tcp"),
804 "compl_code": functools.partial(crossconnect_filter_peer_compl,"tcp"),
808 "from": (TESTBED_ID, ALLFILTERS, "udp"),
809 "to": (None, None, "udp"),
810 "init_code": functools.partial(crossconnect_filter_peer_init,"udp"),
811 "compl_code": functools.partial(crossconnect_filter_peer_compl,"udp"),
817 "forward_X11": dict({
818 "name": "forward_X11",
819 "help": "Forward x11 from main namespace to the node",
820 "type": Attribute.BOOL,
822 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
823 "validation_function": validation.is_bool,
827 "help": "Constrain hostname during resource discovery. May use wildcards.",
828 "type": Attribute.STRING,
829 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
830 "validation_function": validation.is_string,
834 "help": "Constrain location (city) during resource discovery. May use wildcards.",
835 "type": Attribute.STRING,
836 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
837 "validation_function": validation.is_string,
841 "help": "Constrain location (country) during resource discovery. May use wildcards.",
842 "type": Attribute.STRING,
843 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
844 "validation_function": validation.is_string,
848 "help": "Constrain location (region) during resource discovery. May use wildcards.",
849 "type": Attribute.STRING,
850 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
851 "validation_function": validation.is_string,
853 "architecture": dict({
854 "name": "architecture",
855 "help": "Constrain architexture during resource discovery.",
856 "type": Attribute.ENUM,
857 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
858 "allowed": ["x86_64",
860 "validation_function": validation.is_enum,
862 "operating_system": dict({
863 "name": "operatingSystem",
864 "help": "Constrain operating system during resource discovery.",
865 "type": Attribute.ENUM,
866 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
872 "validation_function": validation.is_enum,
876 "help": "Constrain the PlanetLab site this node should reside on.",
877 "type": Attribute.ENUM,
878 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
882 "validation_function": validation.is_enum,
884 "min_reliability": dict({
885 "name": "minReliability",
886 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
887 "type": Attribute.DOUBLE,
889 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
890 "validation_function": validation.is_number,
892 "max_reliability": dict({
893 "name": "maxReliability",
894 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
895 "type": Attribute.DOUBLE,
897 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
898 "validation_function": validation.is_number,
900 "min_bandwidth": dict({
901 "name": "minBandwidth",
902 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
903 "type": Attribute.DOUBLE,
905 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
906 "validation_function": validation.is_number,
908 "max_bandwidth": dict({
909 "name": "maxBandwidth",
910 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
911 "type": Attribute.DOUBLE,
913 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
914 "validation_function": validation.is_number,
918 "help": "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
919 "type": Attribute.DOUBLE,
921 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
922 "validation_function": validation.is_number,
926 "help": "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
927 "type": Attribute.DOUBLE,
929 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
930 "validation_function": validation.is_number,
934 "help": "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
935 "type": Attribute.DOUBLE,
937 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
938 "validation_function": validation.is_number,
942 "help": "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
943 "type": Attribute.DOUBLE,
945 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
946 "validation_function": validation.is_number,
952 "type": Attribute.BOOL,
954 "validation_function": validation.is_bool
958 "help": "This is the primary interface for the attached node",
959 "type": Attribute.BOOL,
961 "validation_function": validation.is_bool
965 "help": "Device name",
966 "type": Attribute.STRING,
967 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
968 "validation_function": validation.is_string
972 "help": "Maximum transmition unit for device",
973 "type": Attribute.INTEGER,
975 "validation_function": validation.is_integer_range(0,1500)
979 "help": "Network mask for the device (eg: 24 for /24 network)",
980 "type": Attribute.INTEGER,
981 "validation_function": validation.is_integer_range(8,24)
985 "help": "Enable SNAT (source NAT to the internet) no this device",
986 "type": Attribute.BOOL,
988 "validation_function": validation.is_bool
992 "help": "Enable multicast forwarding on this device. "
993 "Note that you still need a multicast routing daemon "
995 "type": Attribute.BOOL,
997 "validation_function": validation.is_bool
999 "pointopoint": dict({
1000 "name": "pointopoint",
1001 "help": "If the interface is a P2P link, the remote endpoint's IP "
1002 "should be set on this attribute.",
1003 "type": Attribute.STRING,
1004 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1005 "validation_function": validation.is_string
1009 "help": "Emulated transmission speed (in kbytes per second)",
1010 "type": Attribute.INTEGER,
1011 "range" : (1,10*2**20),
1012 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1013 "validation_function": validation.is_integer
1015 "txqueuelen": dict({
1016 "name": "txqueuelen",
1017 "help": "Transmission queue length (in packets)",
1018 "type": Attribute.INTEGER,
1020 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1021 "range" : (1,10000),
1022 "validation_function": validation.is_integer
1027 "help": "Command line string",
1028 "type": Attribute.STRING,
1029 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1030 "validation_function": validation.is_string
1034 "help": "Run with root privileges",
1035 "type": Attribute.BOOL,
1036 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1038 "validation_function": validation.is_bool
1042 "help": "Standard input",
1043 "type": Attribute.STRING,
1044 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1045 "validation_function": validation.is_string
1050 "help": "Space-separated list of packages required to run the application",
1051 "type": Attribute.STRING,
1052 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1053 "validation_function": validation.is_string
1055 "build-depends": dict({
1056 "name": "buildDepends",
1057 "help": "Space-separated list of packages required to build the application",
1058 "type": Attribute.STRING,
1059 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1060 "validation_function": validation.is_string
1062 "rpm-fusion": dict({
1063 "name": "rpmFusion",
1064 "help": "True if required packages can be found in the RpmFusion repository",
1065 "type": Attribute.BOOL,
1066 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1068 "validation_function": validation.is_bool
1072 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
1073 "Archives won't be expanded automatically.",
1074 "type": Attribute.STRING,
1075 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1076 "validation_function": validation.is_string
1080 "help": "Build commands to execute after deploying the sources. "
1081 "Sources will be in the ${SOURCES} folder. "
1082 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
1083 "Try to make the commands return with a nonzero exit code on error.\n"
1084 "Also, do not install any programs here, use the 'install' attribute. This will "
1085 "help keep the built files constrained to the build folder (which may "
1086 "not be the home folder), and will result in faster deployment. Also, "
1087 "make sure to clean up temporary files, to reduce bandwidth usage between "
1088 "nodes when transferring built packages.",
1089 "type": Attribute.STRING,
1090 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1091 "validation_function": validation.is_string
1095 "help": "Commands to transfer built files to their final destinations. "
1096 "Sources will be in the initial working folder, and a special "
1097 "tag ${SOURCES} can be used to reference the experiment's "
1098 "home folder (where the application commands will run).\n"
1099 "ALL sources and targets needed for execution must be copied there, "
1100 "if building has been enabled.\n"
1101 "That is, 'slave' nodes will not automatically get any source files. "
1102 "'slave' nodes don't get build dependencies either, so if you need "
1103 "make and other tools to install, be sure to provide them as "
1104 "actual dependencies instead.",
1105 "type": Attribute.STRING,
1106 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1107 "validation_function": validation.is_string
1110 "netpipe_mode": dict({
1112 "help": "Link mode:\n"
1113 " * SERVER: applies to incoming connections\n"
1114 " * CLIENT: applies to outgoing connections\n"
1115 " * SERVICE: applies to both",
1116 "type": Attribute.ENUM,
1117 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1118 "allowed": ["SERVER",
1121 "validation_function": validation.is_enum,
1125 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
1126 "type": Attribute.STRING,
1127 "validation_function": is_portlist,
1131 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
1132 "type": Attribute.STRING,
1133 "validation_function": is_addrlist,
1137 "help": "Inbound bandwidth limit (in Mbit/s)",
1138 "type": Attribute.DOUBLE,
1139 "validation_function": validation.is_number,
1143 "help": "Outbound bandwidth limit (in Mbit/s)",
1144 "type": Attribute.DOUBLE,
1145 "validation_function": validation.is_number,
1149 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
1150 "type": Attribute.DOUBLE,
1151 "validation_function": validation.is_number,
1155 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
1156 "type": Attribute.DOUBLE,
1157 "validation_function": validation.is_number,
1161 "help": "Inbound packet delay (in milliseconds)",
1162 "type": Attribute.INTEGER,
1164 "validation_function": validation.is_integer,
1168 "help": "Outbound packet delay (in milliseconds)",
1169 "type": Attribute.INTEGER,
1171 "validation_function": validation.is_integer,
1175 "help": "Path to a .c or .py source for a filter module, or a binary .so",
1176 "type": Attribute.STRING,
1177 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1178 "validation_function": validation.is_string
1182 "help": "Module arguments - comma-separated list of name=value pairs",
1183 "type": Attribute.STRING,
1184 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1185 "validation_function": validation.is_string
1192 "help": "Standard output stream"
1196 "help": "Application standard error",
1200 "help": "Output of the build process",
1203 "netpipe_stats": dict({
1204 "name": "netpipeStats",
1205 "help": "Information about rule match counters, packets dropped, etc.",
1210 "help": "Detailled log of all packets going through the interface",
1214 "help": "PCAP trace of all packets going through the interface",
1218 "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]#}' ",
1220 "dropped_stats": dict({
1221 "name": "dropped_stats",
1222 "help": "Information on dropped packets on a filer or queue associated to a network interface",
1226 create_order = [ INTERNET, NODE, NODEIFACE, CLASSQUEUEFILTER, TOSQUEUEFILTER, TUNFILTER, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
1228 configure_order = [ INTERNET, Parallel(NODE), NODEIFACE, Parallel(TAPIFACE), Parallel(TUNIFACE), NETPIPE, Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(APPLICATION) ]
1230 # Start (and prestart) node after ifaces, because the node needs the ifaces in order to set up routes
1231 start_order = [ INTERNET, NODEIFACE, Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NODE), NETPIPE, Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(APPLICATION) ]
1234 shutdown_order = [ Parallel(APPLICATION), Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NETPIPE), Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), NODEIFACE, Parallel(NODE) ]
1236 factories_info = dict({
1238 "help": "Virtualized Node (V-Server style)",
1239 "category": FC.CATEGORY_NODES,
1240 "create_function": create_node,
1241 "preconfigure_function": configure_node,
1242 "prestart_function": configure_node_routes,
1254 # NEPI-in-NEPI attributes
1255 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
1257 "connector_types": ["devs", "apps", "pipes", "deps"],
1258 "tags": [tags.NODE, tags.ALLOW_ROUTES],
1261 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
1262 "category": FC.CATEGORY_DEVICES,
1263 "create_function": create_nodeiface,
1264 "preconfigure_function": configure_nodeiface,
1265 "box_attributes": [ ],
1266 "connector_types": ["node", "inet"],
1267 "tags": [tags.INTERFACE, tags.HAS_ADDRESSES],
1270 "help": "Virtual TUN network interface (layer 3)",
1271 "category": FC.CATEGORY_DEVICES,
1272 "create_function": create_tuniface,
1273 "preconfigure_function": preconfigure_tuniface,
1274 "configure_function": postconfigure_tuniface,
1275 "prestart_function": wait_tuniface,
1277 "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
1279 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1281 "traces": ["packets", "pcap"],
1282 "connector_types": ["node","udp","tcp","fd->","gre"],
1283 "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
1286 "help": "Virtual TAP network interface (layer 2)",
1287 "category": FC.CATEGORY_DEVICES,
1288 "create_function": create_tapiface,
1289 "preconfigure_function": preconfigure_tuniface,
1290 "configure_function": postconfigure_tuniface,
1291 "prestart_function": wait_tuniface,
1293 "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
1295 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1297 "traces": ["packets", "pcap"],
1298 "connector_types": ["node","udp","tcp","fd->","gre"],
1299 "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
1302 "help": "TUN/TAP stream filter\n\n"
1303 "If specified, it should be either a .py or .so module. "
1304 "It will be loaded, and all incoming and outgoing packets "
1305 "will be routed through it. The filter will not be responsible "
1306 "for buffering, packet queueing is performed in tun_connect "
1307 "already, so it should not concern itself with it. It should "
1308 "not, however, block in one direction if the other is congested.\n"
1310 "Modules are expected to have the following methods:\n"
1312 "\t\tIf arguments are given, this method will be called with the\n"
1313 "\t\tgiven arguments (as keyword args in python modules, or a single\n"
1314 "\taccept_packet(packet, direction):\n"
1315 "\t\tDecide whether to drop the packet. Direction is 0 for packets "
1316 "coming from the local side to the remote, and 1 is for packets "
1317 "coming from the remote side to the local. Return a boolean, "
1318 "true if the packet is not to be dropped.\n"
1319 "\tfilter_init():\n"
1320 "\t\tInitializes a filtering pipe (filter_run). It should "
1321 "return two file descriptors to use as a bidirectional "
1322 "pipe: local and remote. 'local' is where packets from the "
1323 "local side will be written to. After filtering, those packets "
1324 "should be written to 'remote', where tun_connect will read "
1325 "from, and it will forward them to the remote peer. "
1326 "Packets from the remote peer will be written to 'remote', "
1327 "where the filter is expected to read from, and eventually "
1328 "forward them to the local side. If the file descriptors are "
1329 "not nonblocking, they will be set to nonblocking. So it's "
1330 "better to set them from the start like that.\n"
1331 "\tfilter_run(local, remote):\n"
1332 "\t\tIf filter_init is provided, it will be called repeatedly, "
1333 "in a separate thread until the process is killed. It should "
1334 "sleep at most for a second.\n"
1335 "\tfilter_close(local, remote):\n"
1336 "\t\tCalled then the process is killed, if filter_init was provided. "
1337 "It should, among other things, close the file descriptors.\n"
1339 "Python modules are expected to return a tuple in filter_init, "
1340 "either of file descriptors or file objects, while native ones "
1341 "will receive two int*.\n"
1343 "Python modules can additionally contain a custom queue class "
1344 "that will replace the FIFO used by default. The class should "
1345 "be named 'queueclass' and contain an interface compatible with "
1346 "collections.deque. That is, indexing (especiall for q[0]), "
1347 "bool(q), popleft, appendleft, pop (right), append (right), "
1348 "len(q) and clear.",
1349 "category": FC.CATEGORY_CHANNELS,
1350 "create_function": create_tunfilter,
1353 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1355 "connector_types": ["->fd","udp","tcp"],
1357 CLASSQUEUEFILTER : dict({
1358 "help": "TUN classfull queue, uses a separate queue for each user-definable class.\n\n"
1359 "It takes two arguments, both of which have sensible defaults:\n"
1360 "\tsize: the base size of each class' queue\n"
1361 "\tclasses: the class definitions, which follow the following syntax:\n"
1362 '\t <CLASSLIST> ::= <CLASS> ":" CLASSLIST\n'
1364 '\t <CLASS> ::= <PROTOLIST> "*" <PRIORITYSPEC>\n'
1365 '\t | <DFLTCLASS>\n'
1366 '\t <DFLTCLASS> ::= "*" <PRIORITYSPEC>\n'
1367 '\t <PROTOLIST> ::= <PROTO> "." <PROTOLIST>\n'
1369 '\t <PROTO> ::= <NAME> | <NUMBER>\n'
1370 '\t <NAME> ::= --see http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers --\n'
1371 '\t --only in lowercase, with special characters removed--\n'
1372 '\t --or see below--\n'
1373 '\t <NUMBER> ::= [0-9]+\n'
1374 '\t <PRIORITYSPEC> ::= <THOUGHPUT> [ "#" <SIZE> ] [ "p" <PRIORITY> ]\n'
1375 '\t <THOUGHPUT> ::= NUMBER -- default 1\n'
1376 '\t <PRIORITY> ::= NUMBER -- default 0\n'
1377 '\t <SIZE> ::= NUMBER -- default 1\n'
1379 "Size, thoughput and priority are all relative terms. "
1380 "Sizes are multipliers for the size argument, thoughput "
1381 "is applied relative to other classes and the same with "
1383 "category": FC.CATEGORY_CHANNELS,
1384 "create_function": create_classqueuefilter,
1387 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1389 "connector_types": ["->fd","udp","tcp"],
1390 "traces": ["dropped_stats"],
1392 TOSQUEUEFILTER : dict({
1393 "help": "TUN classfull queue that classifies according to the TOS (RFC 791) IP field.\n\n"
1394 "It takes a size argument that specifies the size of each class. As TOS is a "
1395 "subset of DiffServ, this queue half-implements DiffServ.",
1396 "category": FC.CATEGORY_CHANNELS,
1397 "create_function": create_tosqueuefilter,
1400 "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
1402 "connector_types": ["->fd","udp","tcp"],
1406 "help": "Generic executable command line application",
1407 "category": FC.CATEGORY_APPLICATIONS,
1408 "create_function": create_application,
1409 "start_function": start_application,
1410 "status_function": status_application,
1411 "stop_function": stop_application,
1412 "configure_function": configure_application,
1413 "box_attributes": ["command", "sudo", "stdin",
1414 "depends", "build-depends", "build", "install",
1415 "sources", "rpm-fusion" ],
1416 "connector_types": ["node"],
1417 "traces": ["stdout", "stderr", "buildlog", "output"],
1418 "tags": [tags.APPLICATION],
1421 "help": "Requirement for package or application to be installed on some node",
1422 "category": FC.CATEGORY_APPLICATIONS,
1423 "create_function": create_dependency,
1424 "preconfigure_function": configure_dependency,
1425 "box_attributes": ["depends", "build-depends", "build", "install",
1426 "sources", "rpm-fusion" ],
1427 "connector_types": ["node"],
1428 "traces": ["buildlog"],
1430 NEPIDEPENDENCY: dict({
1431 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
1432 "category": FC.CATEGORY_APPLICATIONS,
1433 "create_function": create_nepi_dependency,
1434 "preconfigure_function": configure_dependency,
1435 "box_attributes": [],
1436 "connector_types": ["node"],
1437 "traces": ["buildlog"],
1439 NS3DEPENDENCY: dict({
1440 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
1441 "category": FC.CATEGORY_APPLICATIONS,
1442 "create_function": create_ns3_dependency,
1443 "preconfigure_function": configure_dependency,
1444 "box_attributes": [ ],
1445 "connector_types": ["node"],
1446 "traces": ["buildlog"],
1449 "help": "Internet routing",
1450 "category": FC.CATEGORY_CHANNELS,
1451 "create_function": create_internet,
1452 "connector_types": ["devs"],
1453 "tags": [tags.INTERNET],
1456 "help": "Link emulation",
1457 "category": FC.CATEGORY_CHANNELS,
1458 "create_function": create_netpipe,
1459 "configure_function": configure_netpipe,
1460 "box_attributes": ["netpipe_mode",
1461 "addr_list", "port_list",
1462 "bw_in","plr_in","delay_in",
1463 "bw_out","plr_out","delay_out"],
1464 "connector_types": ["node"],
1465 "traces": ["netpipe_stats"],
1469 testbed_attributes = dict({
1472 "help": "The name of the PlanetLab slice to use",
1473 "type": Attribute.STRING,
1474 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1475 "validation_function": validation.is_string
1479 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
1480 "type": Attribute.STRING,
1481 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1482 "validation_function": validation.is_string
1486 "help": "The PlanetLab user's password.",
1487 "type": Attribute.STRING,
1488 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1489 "validation_function": validation.is_string
1493 "help": "The PlanetLab PLC API host",
1494 "type": Attribute.STRING,
1495 "value": "www.planet-lab.eu",
1496 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1497 "validation_function": validation.is_string
1501 "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.",
1502 "type": Attribute.STRING,
1503 "value": "https://%(hostname)s:443/PLCAPI/",
1504 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1505 "validation_function": validation.is_string
1507 "p2p_deployment": dict({
1508 "name": "p2pDeployment",
1509 "help": "Enable peer-to-peer deployment of applications and dependencies. "
1510 "When enabled, dependency packages and applications are "
1511 "deployed in a P2P fashion, picking a single node to do "
1512 "the building or repo download, while all the others "
1513 "cooperatively exchange resulting binaries or rpms. "
1514 "When deploying to many nodes, this is a far more efficient "
1515 "use of resources. It does require re-encrypting and distributing "
1516 "the slice's private key. Though it is implemented in a secure "
1517 "fashion, if they key's sole purpose is not PlanetLab, then this "
1518 "feature should be disabled.",
1519 "type": Attribute.BOOL,
1521 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1522 "validation_function": validation.is_bool
1524 "slice_ssh_key": dict({
1525 "name": "sliceSSHKey",
1526 "help": "The controller-local path to the slice user's ssh private key. "
1527 "It is the user's responsability to deploy this file where the controller "
1528 "will run, it won't be done automatically because it's sensitive information. "
1529 "It is recommended that a NEPI-specific user be created for this purpose and "
1530 "this purpose alone.",
1531 "type": Attribute.STRING,
1532 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
1533 "validation_function": validation.is_string
1535 "pl_log_level": dict({
1536 "name": "plLogLevel",
1537 "help": "Verbosity of logging of planetlab events.",
1539 "type": Attribute.ENUM,
1540 "allowed": ["DEBUG",
1545 "validation_function": validation.is_enum,
1547 "tap_port_base": dict({
1548 "name": "tapPortBase",
1549 "help": "Base port to use when connecting TUN/TAPs. Effective port will be BASE + GUID.",
1550 "type": Attribute.INTEGER,
1552 "range": (2000,30000),
1553 "validation_function": validation.is_integer_range(2000,30000)
1555 "dedicated_slice": dict({
1556 "name": "dedicatedSlice",
1557 "help": "Set to True if the slice will be dedicated to this experiment. "
1558 "NEPI will perform node and slice cleanup, making sure slices are "
1559 "in a clean, repeatable state before running the experiment.",
1560 "type": Attribute.BOOL,
1562 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
1563 "validation_function": validation.is_bool
1567 supported_recovery_policies = [
1573 class MetadataInfo(metadata.MetadataInfo):
1575 def connector_types(self):
1576 return connector_types
1579 def connections(self):
1583 def attributes(self):
1591 def create_order(self):
1595 def configure_order(self):
1596 return configure_order
1599 def prestart_order(self):
1603 def start_order(self):
1607 def factories_info(self):
1608 return factories_info
1611 def testbed_attributes(self):
1612 return testbed_attributes
1615 def testbed_id(self):
1619 def testbed_version(self):
1620 return TESTBED_VERSION
1623 def supported_recovery_policies(self):
1624 return supported_recovery_policies