2 # -*- coding: utf-8 -*-
6 from constants import TESTBED_ID
7 from nepi.core import metadata
8 from nepi.core.attributes import Attribute
9 from nepi.util import validation
10 from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
11 STATUS_FINISHED, ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP
18 NODEIFACE = "NodeInterface"
19 TUNIFACE = "TunInterface"
20 TAPIFACE = "TapInterface"
21 APPLICATION = "Application"
22 DEPENDENCY = "Dependency"
23 NEPIDEPENDENCY = "NepiDependency"
24 NS3DEPENDENCY = "NS3Dependency"
28 PL_TESTBED_ID = "planetlab"
31 ### Custom validation functions ###
32 def is_addrlist(attribute, value):
33 if not validation.is_string(attribute, value):
40 components = value.split(',')
42 for component in components:
44 addr, mask = component.split('/',1)
46 addr, mask = component, '32'
48 if mask is not None and not (mask and mask.isdigit()):
49 # No empty or nonnumeric masks
52 if not validation.is_ip4_address(attribute, addr):
53 # Address part must be ipv4
58 def is_portlist(attribute, value):
59 if not validation.is_string(attribute, value):
66 components = value.split(',')
68 for component in components:
70 pfrom, pto = component.split('-',1)
72 pfrom = pto = component
74 if not pfrom or not pto or not pfrom.isdigit() or not pto.isdigit():
75 # No empty or nonnumeric ports
81 ### Connection functions ####
83 def connect_node_iface_node(testbed_instance, node_guid, iface_guid):
84 node = testbed_instance._elements[node_guid]
85 iface = testbed_instance._elements[iface_guid]
88 def connect_node_iface_inet(testbed_instance, iface_guid, inet_guid):
89 iface = testbed_instance._elements[iface_guid]
90 iface.has_internet = True
92 def connect_tun_iface_node(testbed_instance, node_guid, iface_guid):
93 node = testbed_instance._elements[node_guid]
94 iface = testbed_instance._elements[iface_guid]
95 if not node.emulation:
96 raise RuntimeError, "Use of TUN interfaces requires emulation"
98 node.required_vsys.update(('fd_tuntap', 'vif_up'))
99 node.required_packages.update(('python', 'python-crypto', 'python-setuptools', 'gcc'))
101 def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid):
102 iface = testbed_instance._elements[iface_guid]
103 peer_iface = testbed_instance._elements[peer_iface_guid]
104 iface.peer_iface = peer_iface
106 iface.tun_proto = proto
107 iface.tun_key = peer_iface.tun_key
109 def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data):
110 iface = testbed_instance._elements[iface_guid]
111 iface.peer_iface = None
112 iface.peer_addr = peer_iface_data.get("tun_addr")
113 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
114 iface.peer_port = peer_iface_data.get("tun_port")
115 iface.tun_key = min(iface.tun_key, peer_iface_data.get("tun_key"))
116 iface.tun_proto = proto
118 preconfigure_tuniface(testbed_instance, iface_guid)
120 def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data):
121 # refresh (refreshable) attributes for second-phase
122 iface = testbed_instance._elements[iface_guid]
123 iface.peer_addr = peer_iface_data.get("tun_addr")
124 iface.peer_proto = peer_iface_data.get("tun_proto") or proto
125 iface.peer_port = peer_iface_data.get("tun_port")
127 postconfigure_tuniface(testbed_instance, iface_guid)
129 def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_iface_data):
130 crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
131 crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
133 def connect_dep(testbed_instance, node_guid, app_guid):
134 node = testbed_instance._elements[node_guid]
135 app = testbed_instance._elements[app_guid]
139 node.required_packages.update(set(
140 app.depends.split() ))
143 if app.home_path and app.home_path not in node.pythonpath:
144 node.pythonpath.append(app.home_path)
147 for envkey, envval in app.env.iteritems():
148 envval = app._replace_paths(envval)
149 node.env[envkey].append(envval)
151 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
152 node = testbed_instance._elements[node_guid]
153 netpipe = testbed_instance._elements[netpipe_guid]
154 if not node.emulation:
155 raise RuntimeError, "Use of NetPipes requires emulation"
159 ### Creation functions ###
161 def create_node(testbed_instance, guid):
162 parameters = testbed_instance._get_parameters(guid)
164 # create element with basic attributes
165 element = testbed_instance._make_node(parameters)
167 # add constraint on number of (real) interfaces
168 # by counting connected devices
169 dev_guids = testbed_instance.get_connected(guid, "devs", "node")
170 num_open_ifaces = sum( # count True values
171 NODEIFACE == testbed_instance._get_factory_id(guid)
172 for guid in dev_guids )
173 element.min_num_external_ifaces = num_open_ifaces
175 # require vroute vsys if we have routes to set up
176 routes = testbed_instance._add_route.get(guid)
178 element.required_vsys.add("vroute")
180 testbed_instance.elements[guid] = element
182 def create_nodeiface(testbed_instance, guid):
183 parameters = testbed_instance._get_parameters(guid)
184 element = testbed_instance._make_node_iface(parameters)
185 testbed_instance.elements[guid] = element
187 def create_tuniface(testbed_instance, guid):
188 parameters = testbed_instance._get_parameters(guid)
189 element = testbed_instance._make_tun_iface(parameters)
191 # Set custom addresses, if there are any already
192 # Setting this early helps set up P2P links
193 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
194 addresses = testbed_instance._add_address[guid]
195 for address in addresses:
196 (address, netprefix, broadcast) = address
197 element.add_address(address, netprefix, broadcast)
199 testbed_instance.elements[guid] = element
201 def create_tapiface(testbed_instance, guid):
202 parameters = testbed_instance._get_parameters(guid)
203 element = testbed_instance._make_tap_iface(parameters)
205 # Set custom addresses, if there are any already
206 # Setting this early helps set up P2P links
207 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
208 addresses = testbed_instance._add_address[guid]
209 for address in addresses:
210 (address, netprefix, broadcast) = address
211 element.add_address(address, netprefix, broadcast)
213 testbed_instance.elements[guid] = element
215 def create_application(testbed_instance, guid):
216 parameters = testbed_instance._get_parameters(guid)
217 element = testbed_instance._make_application(parameters)
219 # Just inject configuration stuff
220 element.home_path = "nepi-app-%s" % (guid,)
222 testbed_instance.elements[guid] = element
224 def create_dependency(testbed_instance, guid):
225 parameters = testbed_instance._get_parameters(guid)
226 element = testbed_instance._make_dependency(parameters)
228 # Just inject configuration stuff
229 element.home_path = "nepi-dep-%s" % (guid,)
231 testbed_instance.elements[guid] = element
233 def create_nepi_dependency(testbed_instance, guid):
234 parameters = testbed_instance._get_parameters(guid)
235 element = testbed_instance._make_nepi_dependency(parameters)
237 # Just inject configuration stuff
238 element.home_path = "nepi-nepi-%s" % (guid,)
240 testbed_instance.elements[guid] = element
242 def create_ns3_dependency(testbed_instance, guid):
243 parameters = testbed_instance._get_parameters(guid)
244 element = testbed_instance._make_ns3_dependency(parameters)
246 # Just inject configuration stuff
247 element.home_path = "nepi-ns3-%s" % (guid,)
249 testbed_instance.elements[guid] = element
251 def create_internet(testbed_instance, guid):
252 parameters = testbed_instance._get_parameters(guid)
253 element = testbed_instance._make_internet(parameters)
254 testbed_instance.elements[guid] = element
256 def create_netpipe(testbed_instance, guid):
257 parameters = testbed_instance._get_parameters(guid)
258 element = testbed_instance._make_netpipe(parameters)
259 testbed_instance.elements[guid] = element
261 ### Start/Stop functions ###
263 def start_application(testbed_instance, guid):
264 parameters = testbed_instance._get_parameters(guid)
265 traces = testbed_instance._get_traces(guid)
266 app = testbed_instance.elements[guid]
268 app.stdout = "stdout" in traces
269 app.stderr = "stderr" in traces
270 app.buildlog = "buildlog" in traces
274 def stop_application(testbed_instance, guid):
275 app = testbed_instance.elements[guid]
278 ### Status functions ###
280 def status_application(testbed_instance, guid):
281 if guid not in testbed_instance.elements.keys():
282 return STATUS_NOT_STARTED
284 app = testbed_instance.elements[guid]
287 ### Configure functions ###
289 def configure_nodeiface(testbed_instance, guid):
290 element = testbed_instance._elements[guid]
292 # Cannot explicitly configure addresses
293 if guid in testbed_instance._add_address:
294 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
297 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
298 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
299 siblings = [ self._element[dev_guid]
300 for dev_guid in dev_guids
301 if dev_guid != guid ]
303 # Fetch address from PLC api
304 element.pick_iface(siblings)
306 # Do some validations
309 def preconfigure_tuniface(testbed_instance, guid):
310 element = testbed_instance._elements[guid]
312 # Set custom addresses if any, and if not set already
313 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
314 addresses = testbed_instance._add_address[guid]
315 for address in addresses:
316 (address, netprefix, broadcast) = address
317 element.add_address(address, netprefix, broadcast)
319 # Link to external interface, if any
320 for iface in testbed_instance._elements.itervalues():
321 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
322 element.external_iface = iface
325 # Set standard TUN attributes
326 if (not element.tun_addr or not element.tun_port) and element.external_iface:
327 element.tun_addr = element.external_iface.address
328 element.tun_port = 15000 + int(guid)
331 traces = testbed_instance._get_traces(guid)
332 element.capture = 'packets' in traces
334 # Do some validations
338 if element.peer_proto:
339 if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface):
341 listening = id(element) < id(element.peer_iface)
344 if not element.tun_addr or not element.tun_port:
346 elif not element.peer_addr or not element.peer_port:
349 # both have addresses...
350 # ...the one with the lesser address listens
351 listening = element.tun_addr < element.peer_addr
356 def postconfigure_tuniface(testbed_instance, guid):
357 element = testbed_instance._elements[guid]
362 def wait_tuniface(testbed_instance, guid):
363 element = testbed_instance._elements[guid]
366 element.async_launch_wait()
369 def configure_node(testbed_instance, guid):
370 node = testbed_instance._elements[guid]
372 # Just inject configuration stuff
373 node.home_path = "nepi-node-%s" % (guid,)
374 node.ident_path = testbed_instance.sliceSSHKey
375 node.slicename = testbed_instance.slicename
377 # Do some validations
380 # recently provisioned nodes may not be up yet
382 while not node.is_alive():
383 time.sleep(sleeptime)
384 sleeptime = min(30.0, sleeptime*1.5)
386 # this will be done in parallel in all nodes
387 # this call only spawns the process
388 node.install_dependencies()
390 def configure_node_routes(testbed_instance, guid):
391 node = testbed_instance._elements[guid]
392 routes = testbed_instance._add_route.get(guid)
396 for dev_guid in testbed_instance.get_connected(guid, "devs", "node")
397 for dev in ( testbed_instance._elements.get(dev_guid) ,)
398 if dev and isinstance(dev, testbed_instance._interfaces.TunIface) ]
400 node.configure_routes(routes, devs)
402 def configure_application(testbed_instance, guid):
403 app = testbed_instance._elements[guid]
405 # Do some validations
408 # Wait for dependencies
409 app.node.wait_dependencies()
414 def configure_dependency(testbed_instance, guid):
415 dep = testbed_instance._elements[guid]
417 # Do some validations
420 # Wait for dependencies
421 dep.node.wait_dependencies()
426 def configure_netpipe(testbed_instance, guid):
427 netpipe = testbed_instance._elements[guid]
429 # Do some validations
432 # Wait for dependencies
433 netpipe.node.wait_dependencies()
438 ### Factory information ###
440 connector_types = dict({
442 "help": "Connector from node to applications",
448 "help": "Connector from node to network interfaces",
454 "help": "Connector from node to application dependencies "
455 "(packages and applications that need to be installed)",
461 "help": "Connector from network interfaces to the internet",
467 "help": "Connector to a Node",
473 "help": "Connector to a NetPipe",
480 "help": "ip-ip tunneling over TCP link",
486 "help": "ip-ip tunneling over UDP datagrams",
492 "help": "TUN device file descriptor provider",
501 "from": (TESTBED_ID, NODE, "devs"),
502 "to": (TESTBED_ID, NODEIFACE, "node"),
503 "init_code": connect_node_iface_node,
507 "from": (TESTBED_ID, NODE, "devs"),
508 "to": (TESTBED_ID, TUNIFACE, "node"),
509 "init_code": connect_tun_iface_node,
513 "from": (TESTBED_ID, NODE, "devs"),
514 "to": (TESTBED_ID, TAPIFACE, "node"),
515 "init_code": connect_tun_iface_node,
519 "from": (TESTBED_ID, NODEIFACE, "inet"),
520 "to": (TESTBED_ID, INTERNET, "devs"),
521 "init_code": connect_node_iface_inet,
525 "from": (TESTBED_ID, NODE, "apps"),
526 "to": (TESTBED_ID, APPLICATION, "node"),
527 "init_code": connect_dep,
531 "from": (TESTBED_ID, NODE, "deps"),
532 "to": (TESTBED_ID, DEPENDENCY, "node"),
533 "init_code": connect_dep,
537 "from": (TESTBED_ID, NODE, "deps"),
538 "to": (TESTBED_ID, NEPIDEPENDENCY, "node"),
539 "init_code": connect_dep,
543 "from": (TESTBED_ID, NODE, "deps"),
544 "to": (TESTBED_ID, NS3DEPENDENCY, "node"),
545 "init_code": connect_dep,
549 "from": (TESTBED_ID, NODE, "pipes"),
550 "to": (TESTBED_ID, NETPIPE, "node"),
551 "init_code": connect_node_netpipe,
555 "from": (TESTBED_ID, TUNIFACE, "tcp"),
556 "to": (TESTBED_ID, TUNIFACE, "tcp"),
557 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
561 "from": (TESTBED_ID, TUNIFACE, "udp"),
562 "to": (TESTBED_ID, TUNIFACE, "udp"),
563 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
567 "from": (TESTBED_ID, TAPIFACE, "tcp"),
568 "to": (TESTBED_ID, TAPIFACE, "tcp"),
569 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
573 "from": (TESTBED_ID, TAPIFACE, "udp"),
574 "to": (TESTBED_ID, TAPIFACE, "udp"),
575 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
579 "from": (TESTBED_ID, TUNIFACE, "tcp"),
580 "to": (None, None, "tcp"),
581 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
582 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
586 "from": (TESTBED_ID, TUNIFACE, "udp"),
587 "to": (None, None, "udp"),
588 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
589 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
593 "from": (TESTBED_ID, TUNIFACE, "fd->"),
594 "to": (None, None, "->fd"),
595 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
599 "from": (TESTBED_ID, TAPIFACE, "tcp"),
600 "to": (None, None, "tcp"),
601 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
602 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
606 "from": (TESTBED_ID, TAPIFACE, "udp"),
607 "to": (None, None, "udp"),
608 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
609 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
613 "from": (TESTBED_ID, TAPIFACE, "fd->"),
614 "to": (None, None, "->fd"),
615 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
621 "forward_X11": dict({
622 "name": "forward_X11",
623 "help": "Forward x11 from main namespace to the node",
624 "type": Attribute.BOOL,
626 "flags": Attribute.DesignOnly,
627 "validation_function": validation.is_bool,
631 "help": "Constrain hostname during resource discovery. May use wildcards.",
632 "type": Attribute.STRING,
633 "flags": Attribute.DesignOnly,
634 "validation_function": validation.is_string,
636 "architecture": dict({
637 "name": "architecture",
638 "help": "Constrain architexture during resource discovery.",
639 "type": Attribute.ENUM,
640 "flags": Attribute.DesignOnly,
641 "allowed": ["x86_64",
643 "validation_function": validation.is_enum,
645 "operating_system": dict({
646 "name": "operatingSystem",
647 "help": "Constrain operating system during resource discovery.",
648 "type": Attribute.ENUM,
649 "flags": Attribute.DesignOnly,
655 "validation_function": validation.is_enum,
659 "help": "Constrain the PlanetLab site this node should reside on.",
660 "type": Attribute.ENUM,
661 "flags": Attribute.DesignOnly,
665 "validation_function": validation.is_enum,
669 "help": "Enable emulation on this node. Enables NetfilterRoutes, bridges, and a host of other functionality.",
670 "type": Attribute.BOOL,
672 "flags": Attribute.DesignOnly,
673 "validation_function": validation.is_bool,
675 "min_reliability": dict({
676 "name": "minReliability",
677 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
678 "type": Attribute.DOUBLE,
680 "flags": Attribute.DesignOnly,
681 "validation_function": validation.is_double,
683 "max_reliability": dict({
684 "name": "maxReliability",
685 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
686 "type": Attribute.DOUBLE,
688 "flags": Attribute.DesignOnly,
689 "validation_function": validation.is_double,
691 "min_bandwidth": dict({
692 "name": "minBandwidth",
693 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
694 "type": Attribute.DOUBLE,
696 "flags": Attribute.DesignOnly,
697 "validation_function": validation.is_double,
699 "max_bandwidth": dict({
700 "name": "maxBandwidth",
701 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
702 "type": Attribute.DOUBLE,
704 "flags": Attribute.DesignOnly,
705 "validation_function": validation.is_double,
711 "type": Attribute.BOOL,
713 "validation_function": validation.is_bool
717 "help": "This is the primary interface for the attached node",
718 "type": Attribute.BOOL,
720 "validation_function": validation.is_bool
722 "device_name": dict({
724 "help": "Device name",
725 "type": Attribute.STRING,
726 "flags": Attribute.DesignOnly,
727 "validation_function": validation.is_string
731 "help": "Maximum transmition unit for device",
732 "type": Attribute.INTEGER,
734 "validation_function": validation.is_integer_range(0,1500)
738 "help": "Network mask for the device (eg: 24 for /24 network)",
739 "type": Attribute.INTEGER,
740 "validation_function": validation.is_integer_range(8,24)
744 "help": "Enable SNAT (source NAT to the internet) no this device",
745 "type": Attribute.BOOL,
747 "validation_function": validation.is_bool
749 "pointopoint": dict({
750 "name": "pointopoint",
751 "help": "If the interface is a P2P link, the remote endpoint's IP "
752 "should be set on this attribute.",
753 "type": Attribute.STRING,
754 "flags": Attribute.DesignOnly,
755 "validation_function": validation.is_string
759 "help": "Transmission queue length (in packets)",
760 "type": Attribute.INTEGER,
761 "flags": Attribute.DesignOnly,
763 "validation_function": validation.is_integer
768 "help": "Command line string",
769 "type": Attribute.STRING,
770 "flags": Attribute.DesignOnly,
771 "validation_function": validation.is_string
775 "help": "Run with root privileges",
776 "type": Attribute.BOOL,
777 "flags": Attribute.DesignOnly,
779 "validation_function": validation.is_bool
783 "help": "Standard input",
784 "type": Attribute.STRING,
785 "flags": Attribute.DesignOnly,
786 "validation_function": validation.is_string
791 "help": "Space-separated list of packages required to run the application",
792 "type": Attribute.STRING,
793 "flags": Attribute.DesignOnly,
794 "validation_function": validation.is_string
796 "build-depends": dict({
797 "name": "buildDepends",
798 "help": "Space-separated list of packages required to build the application",
799 "type": Attribute.STRING,
800 "flags": Attribute.DesignOnly,
801 "validation_function": validation.is_string
805 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
806 "Archives won't be expanded automatically.",
807 "type": Attribute.STRING,
808 "flags": Attribute.DesignOnly,
809 "validation_function": validation.is_string
813 "help": "Build commands to execute after deploying the sources. "
814 "Sources will be in the ${SOURCES} folder. "
815 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
816 "Try to make the commands return with a nonzero exit code on error.\n"
817 "Also, do not install any programs here, use the 'install' attribute. This will "
818 "help keep the built files constrained to the build folder (which may "
819 "not be the home folder), and will result in faster deployment. Also, "
820 "make sure to clean up temporary files, to reduce bandwidth usage between "
821 "nodes when transferring built packages.",
822 "type": Attribute.STRING,
823 "flags": Attribute.DesignOnly,
824 "validation_function": validation.is_string
828 "help": "Commands to transfer built files to their final destinations. "
829 "Sources will be in the initial working folder, and a special "
830 "tag ${SOURCES} can be used to reference the experiment's "
831 "home folder (where the application commands will run).\n"
832 "ALL sources and targets needed for execution must be copied there, "
833 "if building has been enabled.\n"
834 "That is, 'slave' nodes will not automatically get any source files. "
835 "'slave' nodes don't get build dependencies either, so if you need "
836 "make and other tools to install, be sure to provide them as "
837 "actual dependencies instead.",
838 "type": Attribute.STRING,
839 "flags": Attribute.DesignOnly,
840 "validation_function": validation.is_string
843 "netpipe_mode": dict({
845 "help": "Link mode:\n"
846 " * SERVER: applies to incoming connections\n"
847 " * CLIENT: applies to outgoing connections\n"
848 " * SERVICE: applies to both",
849 "type": Attribute.ENUM,
850 "flags": Attribute.DesignOnly,
851 "allowed": ["SERVER",
854 "validation_function": validation.is_enum,
858 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
859 "type": Attribute.STRING,
860 "validation_function": is_portlist,
864 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
865 "type": Attribute.STRING,
866 "validation_function": is_addrlist,
870 "help": "Inbound bandwidth limit (in Mbit/s)",
871 "type": Attribute.DOUBLE,
872 "validation_function": validation.is_double,
876 "help": "Outbound bandwidth limit (in Mbit/s)",
877 "type": Attribute.DOUBLE,
878 "validation_function": validation.is_double,
882 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
883 "type": Attribute.DOUBLE,
884 "validation_function": validation.is_double,
888 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
889 "type": Attribute.DOUBLE,
890 "validation_function": validation.is_double,
894 "help": "Inbound packet delay (in milliseconds)",
895 "type": Attribute.INTEGER,
897 "validation_function": validation.is_integer,
901 "help": "Outbound packet delay (in milliseconds)",
902 "type": Attribute.INTEGER,
904 "validation_function": validation.is_integer,
911 "help": "Standard output stream"
915 "help": "Application standard error",
919 "help": "Output of the build process",
922 "netpipe_stats": dict({
923 "name": "netpipeStats",
924 "help": "Information about rule match counters, packets dropped, etc.",
929 "help": "Detailled log of all packets going through the interface",
933 create_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
935 configure_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
937 # Start (and prestart) node after ifaces, because the node needs the ifaces in order to set up routes
938 start_order = [ INTERNET, NODEIFACE, TAPIFACE, TUNIFACE, NODE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
940 factories_info = dict({
942 "allow_routes": True,
943 "help": "Virtualized Node (V-Server style)",
944 "category": "topology",
945 "create_function": create_node,
946 "preconfigure_function": configure_node,
947 "prestart_function": configure_node_routes,
960 # NEPI-in-NEPI attributes
961 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
963 "connector_types": ["devs", "apps", "pipes", "deps"]
966 "has_addresses": True,
967 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
968 "category": "devices",
969 "create_function": create_nodeiface,
970 "preconfigure_function": configure_nodeiface,
971 "box_attributes": [ ],
972 "connector_types": ["node", "inet"]
975 "allow_addresses": True,
976 "help": "Virtual TUN network interface (layer 3)",
977 "category": "devices",
978 "create_function": create_tuniface,
979 "preconfigure_function": preconfigure_tuniface,
980 "configure_function": postconfigure_tuniface,
981 "prestart_function": wait_tuniface,
983 "up", "device_name", "mtu", "snat", "pointopoint",
985 "tun_proto", "tun_addr", "tun_port", "tun_key"
987 "traces": ["packets"],
988 "connector_types": ["node","udp","tcp","fd->"]
991 "allow_addresses": True,
992 "help": "Virtual TAP network interface (layer 2)",
993 "category": "devices",
994 "create_function": create_tapiface,
995 "preconfigure_function": preconfigure_tuniface,
996 "configure_function": postconfigure_tuniface,
997 "prestart_function": wait_tuniface,
999 "up", "device_name", "mtu", "snat", "pointopoint",
1001 "tun_proto", "tun_addr", "tun_port", "tun_key"
1003 "traces": ["packets"],
1004 "connector_types": ["node","udp","tcp","fd->"]
1007 "help": "Generic executable command line application",
1008 "category": "applications",
1009 "create_function": create_application,
1010 "start_function": start_application,
1011 "status_function": status_application,
1012 "stop_function": stop_application,
1013 "configure_function": configure_application,
1014 "box_attributes": ["command", "sudo", "stdin",
1015 "depends", "build-depends", "build", "install",
1017 "connector_types": ["node"],
1018 "traces": ["stdout", "stderr", "buildlog"]
1021 "help": "Requirement for package or application to be installed on some node",
1022 "category": "applications",
1023 "create_function": create_dependency,
1024 "preconfigure_function": configure_dependency,
1025 "box_attributes": ["depends", "build-depends", "build", "install",
1027 "connector_types": ["node"],
1028 "traces": ["buildlog"]
1030 NEPIDEPENDENCY: dict({
1031 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
1032 "category": "applications",
1033 "create_function": create_nepi_dependency,
1034 "preconfigure_function": configure_dependency,
1035 "box_attributes": [ ],
1036 "connector_types": ["node"],
1037 "traces": ["buildlog"]
1039 NS3DEPENDENCY: dict({
1040 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
1041 "category": "applications",
1042 "create_function": create_ns3_dependency,
1043 "preconfigure_function": configure_dependency,
1044 "box_attributes": [ ],
1045 "connector_types": ["node"],
1046 "traces": ["buildlog"]
1049 "help": "Internet routing",
1050 "category": "topology",
1051 "create_function": create_internet,
1052 "connector_types": ["devs"],
1055 "help": "Link emulation",
1056 "category": "topology",
1057 "create_function": create_netpipe,
1058 "configure_function": configure_netpipe,
1059 "box_attributes": ["netpipe_mode",
1060 "addr_list", "port_list",
1061 "bw_in","plr_in","delay_in",
1062 "bw_out","plr_out","delay_out"],
1063 "connector_types": ["node"],
1064 "traces": ["netpipe_stats"]
1068 testbed_attributes = dict({
1071 "help": "The name of the PlanetLab slice to use",
1072 "type": Attribute.STRING,
1073 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1074 "validation_function": validation.is_string
1078 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
1079 "type": Attribute.STRING,
1080 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1081 "validation_function": validation.is_string
1085 "help": "The PlanetLab user's password.",
1086 "type": Attribute.STRING,
1087 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1088 "validation_function": validation.is_string
1092 "help": "The PlanetLab PLC API host",
1093 "type": Attribute.STRING,
1094 "value": "www.planet-lab.eu",
1095 "flags": Attribute.DesignOnly,
1096 "validation_function": validation.is_string
1100 "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.",
1101 "type": Attribute.STRING,
1102 "value": "https://%(hostname)s:443/PLCAPI/",
1103 "flags": Attribute.DesignOnly,
1104 "validation_function": validation.is_string
1106 "slice_ssh_key": dict({
1107 "name": "sliceSSHKey",
1108 "help": "The controller-local path to the slice user's ssh private key. "
1109 "It is the user's responsability to deploy this file where the controller "
1110 "will run, it won't be done automatically because it's sensitive information. "
1111 "It is recommended that a NEPI-specific user be created for this purpose and "
1112 "this purpose alone.",
1113 "type": Attribute.STRING,
1114 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1115 "validation_function": validation.is_string
1119 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
1121 def connector_types(self):
1122 return connector_types
1125 def connections(self):
1129 def attributes(self):
1137 def create_order(self):
1141 def configure_order(self):
1142 return configure_order
1145 def prestart_order(self):
1149 def start_order(self):
1153 def factories_info(self):
1154 return factories_info
1157 def testbed_attributes(self):
1158 return testbed_attributes