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, value):
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")
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")
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, "node", "devs")
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 testbed_instance.elements[guid] = element
177 def create_nodeiface(testbed_instance, guid):
178 parameters = testbed_instance._get_parameters(guid)
179 element = testbed_instance._make_node_iface(parameters)
180 testbed_instance.elements[guid] = element
182 def create_tuniface(testbed_instance, guid):
183 parameters = testbed_instance._get_parameters(guid)
184 element = testbed_instance._make_tun_iface(parameters)
185 testbed_instance.elements[guid] = element
187 def create_tapiface(testbed_instance, guid):
188 parameters = testbed_instance._get_parameters(guid)
189 element = testbed_instance._make_tap_iface(parameters)
190 testbed_instance.elements[guid] = element
192 def create_application(testbed_instance, guid):
193 parameters = testbed_instance._get_parameters(guid)
194 element = testbed_instance._make_application(parameters)
196 # Just inject configuration stuff
197 element.home_path = "nepi-app-%s" % (guid,)
199 testbed_instance.elements[guid] = element
201 def create_dependency(testbed_instance, guid):
202 parameters = testbed_instance._get_parameters(guid)
203 element = testbed_instance._make_dependency(parameters)
205 # Just inject configuration stuff
206 element.home_path = "nepi-dep-%s" % (guid,)
208 testbed_instance.elements[guid] = element
210 def create_nepi_dependency(testbed_instance, guid):
211 parameters = testbed_instance._get_parameters(guid)
212 element = testbed_instance._make_nepi_dependency(parameters)
214 # Just inject configuration stuff
215 element.home_path = "nepi-nepi-%s" % (guid,)
217 testbed_instance.elements[guid] = element
219 def create_ns3_dependency(testbed_instance, guid):
220 parameters = testbed_instance._get_parameters(guid)
221 element = testbed_instance._make_ns3_dependency(parameters)
223 # Just inject configuration stuff
224 element.home_path = "nepi-ns3-%s" % (guid,)
226 testbed_instance.elements[guid] = element
228 def create_internet(testbed_instance, guid):
229 parameters = testbed_instance._get_parameters(guid)
230 element = testbed_instance._make_internet(parameters)
231 testbed_instance.elements[guid] = element
233 def create_netpipe(testbed_instance, guid):
234 parameters = testbed_instance._get_parameters(guid)
235 element = testbed_instance._make_netpipe(parameters)
236 testbed_instance.elements[guid] = element
238 ### Start/Stop functions ###
240 def start_application(testbed_instance, guid):
241 parameters = testbed_instance._get_parameters(guid)
242 traces = testbed_instance._get_traces(guid)
243 app = testbed_instance.elements[guid]
245 app.stdout = "stdout" in traces
246 app.stderr = "stderr" in traces
247 app.buildlog = "buildlog" in traces
251 def stop_application(testbed_instance, guid):
252 app = testbed_instance.elements[guid]
255 ### Status functions ###
257 def status_application(testbed_instance, guid):
258 if guid not in testbed_instance.elements.keys():
259 return STATUS_NOT_STARTED
261 app = testbed_instance.elements[guid]
264 ### Configure functions ###
266 def configure_nodeiface(testbed_instance, guid):
267 element = testbed_instance._elements[guid]
269 # Cannot explicitly configure addresses
270 if guid in testbed_instance._add_address:
271 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
274 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
275 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
276 siblings = [ self._element[dev_guid]
277 for dev_guid in dev_guids
278 if dev_guid != guid ]
280 # Fetch address from PLC api
281 element.pick_iface(siblings)
283 # Do some validations
286 def preconfigure_tuniface(testbed_instance, guid):
287 element = testbed_instance._elements[guid]
289 # Set custom addresses if any
290 if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix):
291 addresses = testbed_instance._add_address[guid]
292 for address in addresses:
293 (address, netprefix, broadcast) = address
294 element.add_address(address, netprefix, broadcast)
296 # Link to external interface, if any
297 for iface in testbed_instance._elements.itervalues():
298 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
299 element.external_iface = iface
302 # Set standard TUN attributes
303 if (not element.tun_addr or not element.tun_port) and element.external_iface:
304 element.tun_addr = element.external_iface.address
305 element.tun_port = 15000 + int(guid)
308 traces = testbed_instance._get_traces(guid)
309 element.capture = 'packets' in traces
311 # Do some validations
315 if element.peer_proto:
316 if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface):
318 listening = id(element) < id(element.peer_iface)
321 if not element.tun_addr or not element.tun_port:
323 elif not element.peer_addr or not element.peer_port:
326 # both have addresses...
327 # ...the one with the lesser address listens
328 listening = element.tun_addr < element.peer_addr
333 def postconfigure_tuniface(testbed_instance, guid):
334 element = testbed_instance._elements[guid]
340 def configure_node(testbed_instance, guid):
341 node = testbed_instance._elements[guid]
343 # Just inject configuration stuff
344 node.home_path = "nepi-node-%s" % (guid,)
345 node.ident_path = testbed_instance.sliceSSHKey
346 node.slicename = testbed_instance.slicename
348 # Do some validations
351 # recently provisioned nodes may not be up yet
353 while not node.is_alive():
354 time.sleep(sleeptime)
355 sleeptime = min(30.0, sleeptime*1.5)
357 # this will be done in parallel in all nodes
358 # this call only spawns the process
359 node.install_dependencies()
361 def configure_application(testbed_instance, guid):
362 app = testbed_instance._elements[guid]
364 # Do some validations
367 # Wait for dependencies
368 app.node.wait_dependencies()
373 def configure_dependency(testbed_instance, guid):
374 dep = testbed_instance._elements[guid]
376 # Do some validations
379 # Wait for dependencies
380 dep.node.wait_dependencies()
385 def configure_netpipe(testbed_instance, guid):
386 netpipe = testbed_instance._elements[guid]
388 # Do some validations
391 # Wait for dependencies
392 netpipe.node.wait_dependencies()
397 ### Factory information ###
399 connector_types = dict({
401 "help": "Connector from node to applications",
407 "help": "Connector from node to network interfaces",
413 "help": "Connector from node to application dependencies "
414 "(packages and applications that need to be installed)",
420 "help": "Connector from network interfaces to the internet",
426 "help": "Connector to a Node",
432 "help": "Connector to a NetPipe",
439 "help": "ip-ip tunneling over TCP link",
445 "help": "ip-ip tunneling over UDP datagrams",
451 "help": "TUN device file descriptor provider",
460 "from": (TESTBED_ID, NODE, "devs"),
461 "to": (TESTBED_ID, NODEIFACE, "node"),
462 "init_code": connect_node_iface_node,
466 "from": (TESTBED_ID, NODE, "devs"),
467 "to": (TESTBED_ID, TUNIFACE, "node"),
468 "init_code": connect_tun_iface_node,
472 "from": (TESTBED_ID, NODE, "devs"),
473 "to": (TESTBED_ID, TAPIFACE, "node"),
474 "init_code": connect_tun_iface_node,
478 "from": (TESTBED_ID, NODEIFACE, "inet"),
479 "to": (TESTBED_ID, INTERNET, "devs"),
480 "init_code": connect_node_iface_inet,
484 "from": (TESTBED_ID, NODE, "apps"),
485 "to": (TESTBED_ID, APPLICATION, "node"),
486 "init_code": connect_dep,
490 "from": (TESTBED_ID, NODE, "deps"),
491 "to": (TESTBED_ID, DEPENDENCY, "node"),
492 "init_code": connect_dep,
496 "from": (TESTBED_ID, NODE, "deps"),
497 "to": (TESTBED_ID, NEPIDEPENDENCY, "node"),
498 "init_code": connect_dep,
502 "from": (TESTBED_ID, NODE, "deps"),
503 "to": (TESTBED_ID, NS3DEPENDENCY, "node"),
504 "init_code": connect_dep,
508 "from": (TESTBED_ID, NODE, "pipes"),
509 "to": (TESTBED_ID, NETPIPE, "node"),
510 "init_code": connect_node_netpipe,
514 "from": (TESTBED_ID, TUNIFACE, "tcp"),
515 "to": (TESTBED_ID, TUNIFACE, "tcp"),
516 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
520 "from": (TESTBED_ID, TUNIFACE, "udp"),
521 "to": (TESTBED_ID, TUNIFACE, "udp"),
522 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
526 "from": (TESTBED_ID, TAPIFACE, "tcp"),
527 "to": (TESTBED_ID, TAPIFACE, "tcp"),
528 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
532 "from": (TESTBED_ID, TAPIFACE, "udp"),
533 "to": (TESTBED_ID, TAPIFACE, "udp"),
534 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
538 "from": (TESTBED_ID, TUNIFACE, "tcp"),
539 "to": (None, None, "tcp"),
540 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
541 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
545 "from": (TESTBED_ID, TUNIFACE, "udp"),
546 "to": (None, None, "udp"),
547 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
548 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
552 "from": (TESTBED_ID, TUNIFACE, "fd->"),
553 "to": (None, None, "->fd"),
554 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
558 "from": (TESTBED_ID, TAPIFACE, "tcp"),
559 "to": (None, None, "tcp"),
560 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
561 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
565 "from": (TESTBED_ID, TAPIFACE, "udp"),
566 "to": (None, None, "udp"),
567 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
568 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
572 "from": (TESTBED_ID, TAPIFACE, "fd->"),
573 "to": (None, None, "->fd"),
574 "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"),
580 "forward_X11": dict({
581 "name": "forward_X11",
582 "help": "Forward x11 from main namespace to the node",
583 "type": Attribute.BOOL,
585 "flags": Attribute.DesignOnly,
586 "validation_function": validation.is_bool,
590 "help": "Constrain hostname during resource discovery. May use wildcards.",
591 "type": Attribute.STRING,
592 "flags": Attribute.DesignOnly,
593 "validation_function": validation.is_string,
595 "architecture": dict({
596 "name": "architecture",
597 "help": "Constrain architexture during resource discovery.",
598 "type": Attribute.ENUM,
599 "flags": Attribute.DesignOnly,
600 "allowed": ["x86_64",
602 "validation_function": validation.is_enum,
604 "operating_system": dict({
605 "name": "operatingSystem",
606 "help": "Constrain operating system during resource discovery.",
607 "type": Attribute.ENUM,
608 "flags": Attribute.DesignOnly,
614 "validation_function": validation.is_enum,
618 "help": "Constrain the PlanetLab site this node should reside on.",
619 "type": Attribute.ENUM,
620 "flags": Attribute.DesignOnly,
624 "validation_function": validation.is_enum,
628 "help": "Enable emulation on this node. Enables NetfilterRoutes, bridges, and a host of other functionality.",
629 "type": Attribute.BOOL,
631 "flags": Attribute.DesignOnly,
632 "validation_function": validation.is_bool,
634 "min_reliability": dict({
635 "name": "minReliability",
636 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
637 "type": Attribute.DOUBLE,
639 "flags": Attribute.DesignOnly,
640 "validation_function": validation.is_double,
642 "max_reliability": dict({
643 "name": "maxReliability",
644 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
645 "type": Attribute.DOUBLE,
647 "flags": Attribute.DesignOnly,
648 "validation_function": validation.is_double,
650 "min_bandwidth": dict({
651 "name": "minBandwidth",
652 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
653 "type": Attribute.DOUBLE,
655 "flags": Attribute.DesignOnly,
656 "validation_function": validation.is_double,
658 "max_bandwidth": dict({
659 "name": "maxBandwidth",
660 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
661 "type": Attribute.DOUBLE,
663 "flags": Attribute.DesignOnly,
664 "validation_function": validation.is_double,
670 "type": Attribute.BOOL,
672 "validation_function": validation.is_bool
676 "help": "This is the primary interface for the attached node",
677 "type": Attribute.BOOL,
679 "validation_function": validation.is_bool
681 "device_name": dict({
683 "help": "Device name",
684 "type": Attribute.STRING,
685 "flags": Attribute.DesignOnly,
686 "validation_function": validation.is_string
690 "help": "Maximum transmition unit for device",
691 "type": Attribute.INTEGER,
693 "validation_function": validation.is_integer_range(0,1500)
697 "help": "Network mask for the device (eg: 24 for /24 network)",
698 "type": Attribute.INTEGER,
699 "validation_function": validation.is_integer_range(8,24)
703 "help": "Enable SNAT (source NAT to the internet) no this device",
704 "type": Attribute.BOOL,
706 "validation_function": validation.is_bool
710 "help": "Transmission queue length (in packets)",
711 "type": Attribute.INTEGER,
712 "flags": Attribute.DesignOnly,
714 "validation_function": validation.is_integer
719 "help": "Command line string",
720 "type": Attribute.STRING,
721 "flags": Attribute.DesignOnly,
722 "validation_function": validation.is_string
726 "help": "Run with root privileges",
727 "type": Attribute.BOOL,
728 "flags": Attribute.DesignOnly,
730 "validation_function": validation.is_bool
734 "help": "Standard input",
735 "type": Attribute.STRING,
736 "flags": Attribute.DesignOnly,
737 "validation_function": validation.is_string
742 "help": "Space-separated list of packages required to run the application",
743 "type": Attribute.STRING,
744 "flags": Attribute.DesignOnly,
745 "validation_function": validation.is_string
747 "build-depends": dict({
748 "name": "buildDepends",
749 "help": "Space-separated list of packages required to build the application",
750 "type": Attribute.STRING,
751 "flags": Attribute.DesignOnly,
752 "validation_function": validation.is_string
756 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
757 "Archives won't be expanded automatically.",
758 "type": Attribute.STRING,
759 "flags": Attribute.DesignOnly,
760 "validation_function": validation.is_string
764 "help": "Build commands to execute after deploying the sources. "
765 "Sources will be in the ${SOURCES} folder. "
766 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
767 "Try to make the commands return with a nonzero exit code on error.\n"
768 "Also, do not install any programs here, use the 'install' attribute. This will "
769 "help keep the built files constrained to the build folder (which may "
770 "not be the home folder), and will result in faster deployment. Also, "
771 "make sure to clean up temporary files, to reduce bandwidth usage between "
772 "nodes when transferring built packages.",
773 "type": Attribute.STRING,
774 "flags": Attribute.DesignOnly,
775 "validation_function": validation.is_string
779 "help": "Commands to transfer built files to their final destinations. "
780 "Sources will be in the initial working folder, and a special "
781 "tag ${SOURCES} can be used to reference the experiment's "
782 "home folder (where the application commands will run).\n"
783 "ALL sources and targets needed for execution must be copied there, "
784 "if building has been enabled.\n"
785 "That is, 'slave' nodes will not automatically get any source files. "
786 "'slave' nodes don't get build dependencies either, so if you need "
787 "make and other tools to install, be sure to provide them as "
788 "actual dependencies instead.",
789 "type": Attribute.STRING,
790 "flags": Attribute.DesignOnly,
791 "validation_function": validation.is_string
794 "netpipe_mode": dict({
796 "help": "Link mode:\n"
797 " * SERVER: applies to incoming connections\n"
798 " * CLIENT: applies to outgoing connections\n"
799 " * SERVICE: applies to both",
800 "type": Attribute.ENUM,
801 "flags": Attribute.DesignOnly,
802 "allowed": ["SERVER",
805 "validation_function": validation.is_enum,
809 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
810 "type": Attribute.STRING,
811 "validation_function": is_portlist,
815 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
816 "type": Attribute.STRING,
817 "validation_function": is_addrlist,
821 "help": "Inbound bandwidth limit (in Mbit/s)",
822 "type": Attribute.DOUBLE,
823 "validation_function": validation.is_double,
827 "help": "Outbound bandwidth limit (in Mbit/s)",
828 "type": Attribute.DOUBLE,
829 "validation_function": validation.is_double,
833 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
834 "type": Attribute.DOUBLE,
835 "validation_function": validation.is_double,
839 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
840 "type": Attribute.DOUBLE,
841 "validation_function": validation.is_double,
845 "help": "Inbound packet delay (in milliseconds)",
846 "type": Attribute.INTEGER,
848 "validation_function": validation.is_integer,
852 "help": "Outbound packet delay (in milliseconds)",
853 "type": Attribute.INTEGER,
855 "validation_function": validation.is_integer,
862 "help": "Standard output stream"
866 "help": "Application standard error",
870 "help": "Output of the build process",
873 "netpipe_stats": dict({
874 "name": "netpipeStats",
875 "help": "Information about rule match counters, packets dropped, etc.",
880 "help": "Detailled log of all packets going through the interface",
884 create_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
886 configure_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
888 factories_info = dict({
890 "allow_routes": False,
891 "help": "Virtualized Node (V-Server style)",
892 "category": "topology",
893 "create_function": create_node,
894 "preconfigure_function": configure_node,
907 # NEPI-in-NEPI attributes
908 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
910 "connector_types": ["devs", "apps", "pipes", "deps"]
913 "has_addresses": True,
914 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
915 "category": "devices",
916 "create_function": create_nodeiface,
917 "preconfigure_function": configure_nodeiface,
918 "box_attributes": [ ],
919 "connector_types": ["node", "inet"]
922 "allow_addresses": True,
923 "help": "Virtual TUN network interface (layer 3)",
924 "category": "devices",
925 "create_function": create_tuniface,
926 "preconfigure_function": preconfigure_tuniface,
927 "configure_function": postconfigure_tuniface,
929 "up", "device_name", "mtu", "snat",
931 "tun_proto", "tun_addr", "tun_port", "tun_key"
933 "traces": ["packets"],
934 "connector_types": ["node","udp","tcp","fd->"]
937 "allow_addresses": True,
938 "help": "Virtual TAP network interface (layer 2)",
939 "category": "devices",
940 "create_function": create_tapiface,
941 "preconfigure_function": preconfigure_tuniface,
942 "configure_function": postconfigure_tuniface,
944 "up", "device_name", "mtu", "snat",
946 "tun_proto", "tun_addr", "tun_port"
948 "traces": ["packets"],
949 "connector_types": ["node","udp","tcp","fd->"]
952 "help": "Generic executable command line application",
953 "category": "applications",
954 "create_function": create_application,
955 "start_function": start_application,
956 "status_function": status_application,
957 "stop_function": stop_application,
958 "configure_function": configure_application,
959 "box_attributes": ["command", "sudo", "stdin",
960 "depends", "build-depends", "build", "install",
962 "connector_types": ["node"],
963 "traces": ["stdout", "stderr", "buildlog"]
966 "help": "Requirement for package or application to be installed on some node",
967 "category": "applications",
968 "create_function": create_dependency,
969 "preconfigure_function": configure_dependency,
970 "box_attributes": ["depends", "build-depends", "build", "install",
972 "connector_types": ["node"],
973 "traces": ["buildlog"]
975 NEPIDEPENDENCY: dict({
976 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
977 "category": "applications",
978 "create_function": create_nepi_dependency,
979 "preconfigure_function": configure_dependency,
980 "box_attributes": [ ],
981 "connector_types": ["node"],
982 "traces": ["buildlog"]
984 NS3DEPENDENCY: dict({
985 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
986 "category": "applications",
987 "create_function": create_ns3_dependency,
988 "preconfigure_function": configure_dependency,
989 "box_attributes": [ ],
990 "connector_types": ["node"],
991 "traces": ["buildlog"]
994 "help": "Internet routing",
995 "category": "topology",
996 "create_function": create_internet,
997 "connector_types": ["devs"],
1000 "help": "Link emulation",
1001 "category": "topology",
1002 "create_function": create_netpipe,
1003 "configure_function": configure_netpipe,
1004 "box_attributes": ["netpipe_mode",
1005 "addr_list", "port_list",
1006 "bw_in","plr_in","delay_in",
1007 "bw_out","plr_out","delay_out"],
1008 "connector_types": ["node"],
1009 "traces": ["netpipe_stats"]
1013 testbed_attributes = dict({
1016 "help": "The name of the PlanetLab slice to use",
1017 "type": Attribute.STRING,
1018 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1019 "validation_function": validation.is_string
1023 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
1024 "type": Attribute.STRING,
1025 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1026 "validation_function": validation.is_string
1030 "help": "The PlanetLab user's password.",
1031 "type": Attribute.STRING,
1032 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1033 "validation_function": validation.is_string
1035 "slice_ssh_key": dict({
1036 "name": "sliceSSHKey",
1037 "help": "The controller-local path to the slice user's ssh private key. "
1038 "It is the user's responsability to deploy this file where the controller "
1039 "will run, it won't be done automatically because it's sensitive information. "
1040 "It is recommended that a NEPI-specific user be created for this purpose and "
1041 "this purpose alone.",
1042 "type": Attribute.STRING,
1043 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1044 "validation_function": validation.is_string
1048 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
1050 def connector_types(self):
1051 return connector_types
1054 def connections(self):
1058 def attributes(self):
1066 def create_order(self):
1070 def configure_order(self):
1071 return configure_order
1074 def factories_info(self):
1075 return factories_info
1078 def testbed_attributes(self):
1079 return testbed_attributes