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.add('python-crypto')
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 postconfigure_tuniface(testbed_instance, iface_guid)
123 def connect_dep(testbed_instance, node_guid, app_guid):
124 node = testbed_instance._elements[node_guid]
125 app = testbed_instance._elements[app_guid]
129 node.required_packages.update(set(
130 app.depends.split() ))
133 if app.home_path and app.home_path not in node.pythonpath:
134 node.pythonpath.append(app.home_path)
137 for envkey, envval in app.env.iteritems():
138 envval = app._replace_paths(envval)
139 node.env[envkey].append(envval)
141 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
142 node = testbed_instance._elements[node_guid]
143 netpipe = testbed_instance._elements[netpipe_guid]
144 if not node.emulation:
145 raise RuntimeError, "Use of NetPipes requires emulation"
149 ### Creation functions ###
151 def create_node(testbed_instance, guid):
152 parameters = testbed_instance._get_parameters(guid)
154 # create element with basic attributes
155 element = testbed_instance._make_node(parameters)
157 # add constraint on number of (real) interfaces
158 # by counting connected devices
159 dev_guids = testbed_instance.get_connected(guid, "node", "devs")
160 num_open_ifaces = sum( # count True values
161 NODEIFACE == testbed_instance._get_factory_id(guid)
162 for guid in dev_guids )
163 element.min_num_external_ifaces = num_open_ifaces
165 testbed_instance.elements[guid] = element
167 def create_nodeiface(testbed_instance, guid):
168 parameters = testbed_instance._get_parameters(guid)
169 element = testbed_instance._make_node_iface(parameters)
170 testbed_instance.elements[guid] = element
172 def create_tuniface(testbed_instance, guid):
173 parameters = testbed_instance._get_parameters(guid)
174 element = testbed_instance._make_tun_iface(parameters)
175 testbed_instance.elements[guid] = element
177 def create_tapiface(testbed_instance, guid):
178 parameters = testbed_instance._get_parameters(guid)
179 element = testbed_instance._make_tap_iface(parameters)
180 testbed_instance.elements[guid] = element
182 def create_application(testbed_instance, guid):
183 parameters = testbed_instance._get_parameters(guid)
184 element = testbed_instance._make_application(parameters)
186 # Just inject configuration stuff
187 element.home_path = "nepi-app-%s" % (guid,)
189 testbed_instance.elements[guid] = element
191 def create_dependency(testbed_instance, guid):
192 parameters = testbed_instance._get_parameters(guid)
193 element = testbed_instance._make_dependency(parameters)
195 # Just inject configuration stuff
196 element.home_path = "nepi-dep-%s" % (guid,)
198 testbed_instance.elements[guid] = element
200 def create_nepi_dependency(testbed_instance, guid):
201 parameters = testbed_instance._get_parameters(guid)
202 element = testbed_instance._make_nepi_dependency(parameters)
204 # Just inject configuration stuff
205 element.home_path = "nepi-nepi-%s" % (guid,)
207 testbed_instance.elements[guid] = element
209 def create_ns3_dependency(testbed_instance, guid):
210 parameters = testbed_instance._get_parameters(guid)
211 element = testbed_instance._make_ns3_dependency(parameters)
213 # Just inject configuration stuff
214 element.home_path = "nepi-ns3-%s" % (guid,)
216 testbed_instance.elements[guid] = element
218 def create_internet(testbed_instance, guid):
219 parameters = testbed_instance._get_parameters(guid)
220 element = testbed_instance._make_internet(parameters)
221 testbed_instance.elements[guid] = element
223 def create_netpipe(testbed_instance, guid):
224 parameters = testbed_instance._get_parameters(guid)
225 element = testbed_instance._make_netpipe(parameters)
226 testbed_instance.elements[guid] = element
228 ### Start/Stop functions ###
230 def start_application(testbed_instance, guid):
231 parameters = testbed_instance._get_parameters(guid)
232 traces = testbed_instance._get_traces(guid)
233 app = testbed_instance.elements[guid]
235 app.stdout = "stdout" in traces
236 app.stderr = "stderr" in traces
237 app.buildlog = "buildlog" in traces
241 def stop_application(testbed_instance, guid):
242 app = testbed_instance.elements[guid]
245 ### Status functions ###
247 def status_application(testbed_instance, guid):
248 if guid not in testbed_instance.elements.keys():
249 return STATUS_NOT_STARTED
251 app = testbed_instance.elements[guid]
254 ### Configure functions ###
256 def configure_nodeiface(testbed_instance, guid):
257 element = testbed_instance._elements[guid]
259 # Cannot explicitly configure addresses
260 if guid in testbed_instance._add_address:
261 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
264 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
265 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
266 siblings = [ self._element[dev_guid]
267 for dev_guid in dev_guids
268 if dev_guid != guid ]
270 # Fetch address from PLC api
271 element.pick_iface(siblings)
273 # Do some validations
276 def preconfigure_tuniface(testbed_instance, guid):
277 element = testbed_instance._elements[guid]
279 # Set custom addresses if any
280 if guid in testbed_instance._add_address:
281 addresses = testbed_instance._add_address[guid]
282 for address in addresses:
283 (address, netprefix, broadcast) = address
284 element.add_address(address, netprefix, broadcast)
286 # Link to external interface, if any
287 for iface in testbed_instance._elements.itervalues():
288 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
289 element.external_iface = iface
292 # Set standard TUN attributes
293 if (not element.tun_addr or not element.tun_port) and element.external_iface:
294 element.tun_addr = element.external_iface.address
295 element.tun_port = 15000 + int(guid)
298 traces = testbed_instance._get_traces(guid)
299 element.capture = 'packets' in traces
301 # Do some validations
305 if element.peer_proto:
306 if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface):
308 listening = id(element) < id(element.peer_iface)
311 if not element.tun_addr or not element.tun_port:
313 elif not element.peer_addr or not element.peer_port:
316 # both have addresses...
317 # ...the one with the lesser address listens
318 listening = element.tun_addr < element.peer_addr
323 def postconfigure_tuniface(testbed_instance, guid):
324 element = testbed_instance._elements[guid]
330 def configure_node(testbed_instance, guid):
331 node = testbed_instance._elements[guid]
333 # Just inject configuration stuff
334 node.home_path = "nepi-node-%s" % (guid,)
335 node.ident_path = testbed_instance.sliceSSHKey
336 node.slicename = testbed_instance.slicename
338 # Do some validations
341 # recently provisioned nodes may not be up yet
343 while not node.is_alive():
344 time.sleep(sleeptime)
345 sleeptime = min(30.0, sleeptime*1.5)
347 # this will be done in parallel in all nodes
348 # this call only spawns the process
349 node.install_dependencies()
351 def configure_application(testbed_instance, guid):
352 app = testbed_instance._elements[guid]
354 # Do some validations
357 # Wait for dependencies
358 app.node.wait_dependencies()
363 def configure_dependency(testbed_instance, guid):
364 dep = testbed_instance._elements[guid]
366 # Do some validations
369 # Wait for dependencies
370 dep.node.wait_dependencies()
375 def configure_netpipe(testbed_instance, guid):
376 netpipe = testbed_instance._elements[guid]
378 # Do some validations
381 # Wait for dependencies
382 netpipe.node.wait_dependencies()
387 ### Factory information ###
389 connector_types = dict({
391 "help": "Connector from node to applications",
397 "help": "Connector from node to network interfaces",
403 "help": "Connector from node to application dependencies "
404 "(packages and applications that need to be installed)",
410 "help": "Connector from network interfaces to the internet",
416 "help": "Connector to a Node",
422 "help": "Connector to a NetPipe",
429 "help": "ip-ip tunneling over TCP link",
435 "help": "ip-ip tunneling over UDP datagrams",
444 "from": (TESTBED_ID, NODE, "devs"),
445 "to": (TESTBED_ID, NODEIFACE, "node"),
446 "init_code": connect_node_iface_node,
450 "from": (TESTBED_ID, NODE, "devs"),
451 "to": (TESTBED_ID, TUNIFACE, "node"),
452 "init_code": connect_tun_iface_node,
456 "from": (TESTBED_ID, NODE, "devs"),
457 "to": (TESTBED_ID, TAPIFACE, "node"),
458 "init_code": connect_tun_iface_node,
462 "from": (TESTBED_ID, NODEIFACE, "inet"),
463 "to": (TESTBED_ID, INTERNET, "devs"),
464 "init_code": connect_node_iface_inet,
468 "from": (TESTBED_ID, NODE, "apps"),
469 "to": (TESTBED_ID, APPLICATION, "node"),
470 "init_code": connect_dep,
474 "from": (TESTBED_ID, NODE, "deps"),
475 "to": (TESTBED_ID, DEPENDENCY, "node"),
476 "init_code": connect_dep,
480 "from": (TESTBED_ID, NODE, "deps"),
481 "to": (TESTBED_ID, NEPIDEPENDENCY, "node"),
482 "init_code": connect_dep,
486 "from": (TESTBED_ID, NODE, "deps"),
487 "to": (TESTBED_ID, NS3DEPENDENCY, "node"),
488 "init_code": connect_dep,
492 "from": (TESTBED_ID, NODE, "pipes"),
493 "to": (TESTBED_ID, NETPIPE, "node"),
494 "init_code": connect_node_netpipe,
498 "from": (TESTBED_ID, TUNIFACE, "tcp"),
499 "to": (TESTBED_ID, TUNIFACE, "tcp"),
500 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
504 "from": (TESTBED_ID, TUNIFACE, "udp"),
505 "to": (TESTBED_ID, TUNIFACE, "udp"),
506 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
510 "from": (TESTBED_ID, TAPIFACE, "tcp"),
511 "to": (TESTBED_ID, TAPIFACE, "tcp"),
512 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
516 "from": (TESTBED_ID, TAPIFACE, "udp"),
517 "to": (TESTBED_ID, TAPIFACE, "udp"),
518 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
522 "from": (TESTBED_ID, TUNIFACE, "tcp"),
523 "to": (None, None, "tcp"),
524 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
525 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
529 "from": (TESTBED_ID, TUNIFACE, "udp"),
530 "to": (None, None, "udp"),
531 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
532 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
536 "from": (TESTBED_ID, TAPIFACE, "tcp"),
537 "to": (None, None, "tcp"),
538 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
539 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
543 "from": (TESTBED_ID, TAPIFACE, "udp"),
544 "to": (None, None, "udp"),
545 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
546 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
552 "forward_X11": dict({
553 "name": "forward_X11",
554 "help": "Forward x11 from main namespace to the node",
555 "type": Attribute.BOOL,
557 "flags": Attribute.DesignOnly,
558 "validation_function": validation.is_bool,
562 "help": "Constrain hostname during resource discovery. May use wildcards.",
563 "type": Attribute.STRING,
564 "flags": Attribute.DesignOnly,
565 "validation_function": validation.is_string,
567 "architecture": dict({
568 "name": "architecture",
569 "help": "Constrain architexture during resource discovery.",
570 "type": Attribute.ENUM,
571 "flags": Attribute.DesignOnly,
572 "allowed": ["x86_64",
574 "validation_function": validation.is_enum,
576 "operating_system": dict({
577 "name": "operatingSystem",
578 "help": "Constrain operating system during resource discovery.",
579 "type": Attribute.ENUM,
580 "flags": Attribute.DesignOnly,
586 "validation_function": validation.is_enum,
590 "help": "Constrain the PlanetLab site this node should reside on.",
591 "type": Attribute.ENUM,
592 "flags": Attribute.DesignOnly,
596 "validation_function": validation.is_enum,
600 "help": "Enable emulation on this node. Enables NetfilterRoutes, bridges, and a host of other functionality.",
601 "type": Attribute.BOOL,
603 "flags": Attribute.DesignOnly,
604 "validation_function": validation.is_bool,
606 "min_reliability": dict({
607 "name": "minReliability",
608 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
609 "type": Attribute.DOUBLE,
611 "flags": Attribute.DesignOnly,
612 "validation_function": validation.is_double,
614 "max_reliability": dict({
615 "name": "maxReliability",
616 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
617 "type": Attribute.DOUBLE,
619 "flags": Attribute.DesignOnly,
620 "validation_function": validation.is_double,
622 "min_bandwidth": dict({
623 "name": "minBandwidth",
624 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
625 "type": Attribute.DOUBLE,
627 "flags": Attribute.DesignOnly,
628 "validation_function": validation.is_double,
630 "max_bandwidth": dict({
631 "name": "maxBandwidth",
632 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
633 "type": Attribute.DOUBLE,
635 "flags": Attribute.DesignOnly,
636 "validation_function": validation.is_double,
642 "type": Attribute.BOOL,
644 "validation_function": validation.is_bool
648 "help": "This is the primary interface for the attached node",
649 "type": Attribute.BOOL,
651 "validation_function": validation.is_bool
653 "device_name": dict({
655 "help": "Device name",
656 "type": Attribute.STRING,
657 "flags": Attribute.DesignOnly,
658 "validation_function": validation.is_string
662 "help": "Maximum transmition unit for device",
663 "type": Attribute.INTEGER,
665 "validation_function": validation.is_integer_range(0,1500)
669 "help": "Network mask for the device (eg: 24 for /24 network)",
670 "type": Attribute.INTEGER,
671 "validation_function": validation.is_integer_range(8,24)
675 "help": "Enable SNAT (source NAT to the internet) no this device",
676 "type": Attribute.BOOL,
678 "validation_function": validation.is_bool
682 "help": "Transmission queue length (in packets)",
683 "type": Attribute.INTEGER,
684 "flags": Attribute.DesignOnly,
686 "validation_function": validation.is_integer
691 "help": "Command line string",
692 "type": Attribute.STRING,
693 "flags": Attribute.DesignOnly,
694 "validation_function": validation.is_string
698 "help": "Run with root privileges",
699 "type": Attribute.BOOL,
700 "flags": Attribute.DesignOnly,
702 "validation_function": validation.is_bool
706 "help": "Standard input",
707 "type": Attribute.STRING,
708 "flags": Attribute.DesignOnly,
709 "validation_function": validation.is_string
714 "help": "Space-separated list of packages required to run the application",
715 "type": Attribute.STRING,
716 "flags": Attribute.DesignOnly,
717 "validation_function": validation.is_string
719 "build-depends": dict({
720 "name": "buildDepends",
721 "help": "Space-separated list of packages required to build the application",
722 "type": Attribute.STRING,
723 "flags": Attribute.DesignOnly,
724 "validation_function": validation.is_string
728 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
729 "Archives won't be expanded automatically.",
730 "type": Attribute.STRING,
731 "flags": Attribute.DesignOnly,
732 "validation_function": validation.is_string
736 "help": "Build commands to execute after deploying the sources. "
737 "Sources will be in the ${SOURCES} folder. "
738 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
739 "Try to make the commands return with a nonzero exit code on error.\n"
740 "Also, do not install any programs here, use the 'install' attribute. This will "
741 "help keep the built files constrained to the build folder (which may "
742 "not be the home folder), and will result in faster deployment. Also, "
743 "make sure to clean up temporary files, to reduce bandwidth usage between "
744 "nodes when transferring built packages.",
745 "type": Attribute.STRING,
746 "flags": Attribute.DesignOnly,
747 "validation_function": validation.is_string
751 "help": "Commands to transfer built files to their final destinations. "
752 "Sources will be in the initial working folder, and a special "
753 "tag ${SOURCES} can be used to reference the experiment's "
754 "home folder (where the application commands will run).\n"
755 "ALL sources and targets needed for execution must be copied there, "
756 "if building has been enabled.\n"
757 "That is, 'slave' nodes will not automatically get any source files. "
758 "'slave' nodes don't get build dependencies either, so if you need "
759 "make and other tools to install, be sure to provide them as "
760 "actual dependencies instead.",
761 "type": Attribute.STRING,
762 "flags": Attribute.DesignOnly,
763 "validation_function": validation.is_string
765 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP: dict({
766 "name": ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
767 "help": "Commands to set up the environment needed to run NEPI testbeds",
768 "type": Attribute.STRING,
769 "flags": Attribute.Invisible | Attribute.ReadOnly,
770 "validation_function": validation.is_string
773 "netpipe_mode": dict({
775 "help": "Link mode:\n"
776 " * SERVER: applies to incoming connections\n"
777 " * CLIENT: applies to outgoing connections\n"
778 " * SERVICE: applies to both",
779 "type": Attribute.ENUM,
780 "flags": Attribute.DesignOnly,
781 "allowed": ["SERVER",
784 "validation_function": validation.is_enum,
788 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
789 "type": Attribute.STRING,
790 "validation_function": is_portlist,
794 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
795 "type": Attribute.STRING,
796 "validation_function": is_addrlist,
800 "help": "Inbound bandwidth limit (in Mbit/s)",
801 "type": Attribute.DOUBLE,
802 "validation_function": validation.is_double,
806 "help": "Outbound bandwidth limit (in Mbit/s)",
807 "type": Attribute.DOUBLE,
808 "validation_function": validation.is_double,
812 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
813 "type": Attribute.DOUBLE,
814 "validation_function": validation.is_double,
818 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
819 "type": Attribute.DOUBLE,
820 "validation_function": validation.is_double,
824 "help": "Inbound packet delay (in milliseconds)",
825 "type": Attribute.INTEGER,
827 "validation_function": validation.is_integer,
831 "help": "Outbound packet delay (in milliseconds)",
832 "type": Attribute.INTEGER,
834 "validation_function": validation.is_integer,
841 "help": "Standard output stream"
845 "help": "Application standard error",
849 "help": "Output of the build process",
852 "netpipe_stats": dict({
853 "name": "netpipeStats",
854 "help": "Information about rule match counters, packets dropped, etc.",
859 "help": "Detailled log of all packets going through the interface",
863 create_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
865 configure_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
867 factories_info = dict({
869 "allow_routes": False,
870 "help": "Virtualized Node (V-Server style)",
871 "category": "topology",
872 "create_function": create_node,
873 "preconfigure_function": configure_node,
886 # NEPI-in-NEPI attributes
887 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
889 "connector_types": ["devs", "apps", "pipes", "deps"]
892 "has_addresses": True,
893 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
894 "category": "devices",
895 "create_function": create_nodeiface,
896 "preconfigure_function": configure_nodeiface,
897 "box_attributes": [ ],
898 "connector_types": ["node", "inet"]
901 "allow_addresses": True,
902 "help": "Virtual TUN network interface (layer 3)",
903 "category": "devices",
904 "create_function": create_tuniface,
905 "preconfigure_function": preconfigure_tuniface,
906 "configure_function": postconfigure_tuniface,
908 "up", "device_name", "mtu", "snat",
910 "tun_proto", "tun_addr", "tun_port", "tun_key"
912 "traces": ["packets"],
913 "connector_types": ["node","udp","tcp"]
916 "allow_addresses": True,
917 "help": "Virtual TAP network interface (layer 2)",
918 "category": "devices",
919 "create_function": create_tapiface,
920 "preconfigure_function": preconfigure_tuniface,
921 "configure_function": postconfigure_tuniface,
923 "up", "device_name", "mtu", "snat",
925 "tun_proto", "tun_addr", "tun_port"
927 "traces": ["packets"],
928 "connector_types": ["node","udp","tcp"]
931 "help": "Generic executable command line application",
932 "category": "applications",
933 "create_function": create_application,
934 "start_function": start_application,
935 "status_function": status_application,
936 "stop_function": stop_application,
937 "configure_function": configure_application,
938 "box_attributes": ["command", "sudo", "stdin",
939 "depends", "build-depends", "build", "install",
941 "connector_types": ["node"],
942 "traces": ["stdout", "stderr", "buildlog"]
945 "help": "Requirement for package or application to be installed on some node",
946 "category": "applications",
947 "create_function": create_dependency,
948 "configure_function": configure_dependency,
949 "box_attributes": ["depends", "build-depends", "build", "install",
951 "connector_types": ["node"],
952 "traces": ["buildlog"]
954 NEPIDEPENDENCY: dict({
955 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
956 "category": "applications",
957 "create_function": create_nepi_dependency,
958 "configure_function": configure_dependency,
959 "box_attributes": [ ],
960 "connector_types": ["node"],
961 "traces": ["buildlog"]
963 NS3DEPENDENCY: dict({
964 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
965 "category": "applications",
966 "create_function": create_ns3_dependency,
967 "configure_function": configure_dependency,
968 "box_attributes": [ ],
969 "connector_types": ["node"],
970 "traces": ["buildlog"]
973 "help": "Internet routing",
974 "category": "topology",
975 "create_function": create_internet,
976 "connector_types": ["devs"],
979 "help": "Link emulation",
980 "category": "topology",
981 "create_function": create_netpipe,
982 "configure_function": configure_netpipe,
983 "box_attributes": ["netpipe_mode",
984 "addr_list", "port_list",
985 "bw_in","plr_in","delay_in",
986 "bw_out","plr_out","delay_out"],
987 "connector_types": ["node"],
988 "traces": ["netpipe_stats"]
992 testbed_attributes = dict({
995 "help": "The name of the PlanetLab slice to use",
996 "type": Attribute.STRING,
997 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
998 "validation_function": validation.is_string
1002 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
1003 "type": Attribute.STRING,
1004 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1005 "validation_function": validation.is_string
1009 "help": "The PlanetLab user's password.",
1010 "type": Attribute.STRING,
1011 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1012 "validation_function": validation.is_string
1014 "slice_ssh_key": dict({
1015 "name": "sliceSSHKey",
1016 "help": "The controller-local path to the slice user's ssh private key. "
1017 "It is the user's responsability to deploy this file where the controller "
1018 "will run, it won't be done automatically because it's sensitive information. "
1019 "It is recommended that a NEPI-specific user be created for this purpose and "
1020 "this purpose alone.",
1021 "type": Attribute.STRING,
1022 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
1023 "validation_function": validation.is_string
1027 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
1029 def connector_types(self):
1030 return connector_types
1033 def connections(self):
1037 def attributes(self):
1045 def create_order(self):
1049 def configure_order(self):
1050 return configure_order
1053 def factories_info(self):
1054 return factories_info
1057 def testbed_attributes(self):
1058 return testbed_attributes