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 APPLICATION = "Application"
21 DEPENDENCY = "Dependency"
22 NEPIDEPENDENCY = "NepiDependency"
23 NS3DEPENDENCY = "NS3Dependency"
27 PL_TESTBED_ID = "planetlab"
30 ### Custom validation functions ###
31 def is_addrlist(attribute, value):
32 if not validation.is_string(attribute, value):
39 components = value.split(',')
41 for component in components:
43 addr, mask = component.split('/',1)
45 addr, mask = component, 32
47 if mask is not None and not (mask and mask.isdigit()):
48 # No empty or nonnumeric masks
51 if not validation.is_ip4_address(attribute, value):
52 # Address part must be ipv4
57 def is_portlist(attribute, value):
58 if not validation.is_string(attribute, value):
65 components = value.split(',')
67 for component in components:
69 pfrom, pto = component.split('-',1)
71 pfrom = pto = component
73 if not pfrom or not pto or not pfrom.isdigit() or not pto.isdigit():
74 # No empty or nonnumeric ports
80 ### Connection functions ####
82 def connect_node_iface_node(testbed_instance, node_guid, iface_guid):
83 node = testbed_instance._elements[node_guid]
84 iface = testbed_instance._elements[iface_guid]
87 def connect_node_iface_inet(testbed_instance, iface_guid, inet_guid):
88 iface = testbed_instance._elements[iface_guid]
89 iface.has_internet = True
91 def connect_tun_iface_node(testbed_instance, node_guid, iface_guid):
92 node = testbed_instance._elements[node_guid]
93 iface = testbed_instance._elements[iface_guid]
94 if not node.emulation:
95 raise RuntimeError, "Use of TUN interfaces requires emulation"
97 node.required_vsys.update(('fd_tuntap', 'vif_up'))
99 def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid):
100 iface = testbed_instance._elements[iface_guid]
101 peer_iface = testbed_instance._elements[peer_iface_guid]
102 iface.peer_iface = peer_iface
104 iface.tun_proto = proto
106 def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data):
107 iface = testbed_instance._elements[iface_guid]
108 iface.peer_iface = None
109 iface.peer_addr = peer_iface_data.get("tun_addr")
110 iface.peer_proto = peer_iface_data.get("tun_proto")
111 iface.peer_port = peer_iface_data.get("tun_port")
112 iface.tun_proto = proto
114 preconfigure_tuniface(testbed_instance, iface_guid)
116 def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data):
117 postconfigure_tuniface(testbed_instance, iface_guid)
119 def connect_dep(testbed_instance, node_guid, app_guid):
120 node = testbed_instance._elements[node_guid]
121 app = testbed_instance._elements[app_guid]
125 node.required_packages.update(set(
126 app.depends.split() ))
129 if app.home_path and app.home_path not in node.pythonpath:
130 node.pythonpath.append(app.home_path)
133 for envkey, envval in app.env.iteritems():
134 envval = app._replace_paths(envval)
135 node.env[envkey].append(envval)
137 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
138 node = testbed_instance._elements[node_guid]
139 netpipe = testbed_instance._elements[netpipe_guid]
140 if not node.emulation:
141 raise RuntimeError, "Use of NetPipes requires emulation"
145 ### Creation functions ###
147 def create_node(testbed_instance, guid):
148 parameters = testbed_instance._get_parameters(guid)
150 # create element with basic attributes
151 element = testbed_instance._make_node(parameters)
153 # add constraint on number of (real) interfaces
154 # by counting connected devices
155 dev_guids = testbed_instance.get_connected(guid, "node", "devs")
156 num_open_ifaces = sum( # count True values
157 NODEIFACE == testbed_instance._get_factory_id(guid)
158 for guid in dev_guids )
159 element.min_num_external_ifaces = num_open_ifaces
161 testbed_instance.elements[guid] = element
163 def create_nodeiface(testbed_instance, guid):
164 parameters = testbed_instance._get_parameters(guid)
165 element = testbed_instance._make_node_iface(parameters)
166 testbed_instance.elements[guid] = element
168 def create_tuniface(testbed_instance, guid):
169 parameters = testbed_instance._get_parameters(guid)
170 element = testbed_instance._make_tun_iface(parameters)
171 testbed_instance.elements[guid] = element
173 def create_application(testbed_instance, guid):
174 parameters = testbed_instance._get_parameters(guid)
175 element = testbed_instance._make_application(parameters)
177 # Just inject configuration stuff
178 element.home_path = "nepi-app-%s" % (guid,)
180 testbed_instance.elements[guid] = element
182 def create_dependency(testbed_instance, guid):
183 parameters = testbed_instance._get_parameters(guid)
184 element = testbed_instance._make_dependency(parameters)
186 # Just inject configuration stuff
187 element.home_path = "nepi-dep-%s" % (guid,)
189 testbed_instance.elements[guid] = element
191 def create_nepi_dependency(testbed_instance, guid):
192 parameters = testbed_instance._get_parameters(guid)
193 element = testbed_instance._make_nepi_dependency(parameters)
195 # Just inject configuration stuff
196 element.home_path = "nepi-nepi-%s" % (guid,)
198 testbed_instance.elements[guid] = element
200 def create_ns3_dependency(testbed_instance, guid):
201 parameters = testbed_instance._get_parameters(guid)
202 element = testbed_instance._make_ns3_dependency(parameters)
204 # Just inject configuration stuff
205 element.home_path = "nepi-ns3-%s" % (guid,)
207 testbed_instance.elements[guid] = element
209 def create_internet(testbed_instance, guid):
210 parameters = testbed_instance._get_parameters(guid)
211 element = testbed_instance._make_internet(parameters)
212 testbed_instance.elements[guid] = element
214 def create_netpipe(testbed_instance, guid):
215 parameters = testbed_instance._get_parameters(guid)
216 element = testbed_instance._make_netpipe(parameters)
217 testbed_instance.elements[guid] = element
219 ### Start/Stop functions ###
221 def start_application(testbed_instance, guid):
222 parameters = testbed_instance._get_parameters(guid)
223 traces = testbed_instance._get_traces(guid)
224 app = testbed_instance.elements[guid]
226 app.stdout = "stdout" in traces
227 app.stderr = "stderr" in traces
228 app.buildlog = "buildlog" in traces
232 def stop_application(testbed_instance, guid):
233 app = testbed_instance.elements[guid]
236 ### Status functions ###
238 def status_application(testbed_instance, guid):
239 if guid not in testbed_instance.elements.keys():
240 return STATUS_NOT_STARTED
242 app = testbed_instance.elements[guid]
245 ### Configure functions ###
247 def configure_nodeiface(testbed_instance, guid):
248 element = testbed_instance._elements[guid]
250 # Cannot explicitly configure addresses
251 if guid in testbed_instance._add_address:
252 raise ValueError, "Cannot explicitly set address of public PlanetLab interface"
255 node_guid = testbed_instance.get_connected(guid, "node", "devs")[0]
256 dev_guids = testbed_instance.get_connected(node_guid, "node", "devs")
257 siblings = [ self._element[dev_guid]
258 for dev_guid in dev_guids
259 if dev_guid != guid ]
261 # Fetch address from PLC api
262 element.pick_iface(siblings)
264 # Do some validations
267 def preconfigure_tuniface(testbed_instance, guid):
268 element = testbed_instance._elements[guid]
270 # Set custom addresses if any
271 if guid in testbed_instance._add_address:
272 addresses = testbed_instance._add_address[guid]
273 for address in addresses:
274 (address, netprefix, broadcast) = address
275 element.add_address(address, netprefix, broadcast)
277 # Link to external interface, if any
278 for iface in testbed_instance._elements.itervalues():
279 if isinstance(iface, testbed_instance._interfaces.NodeIface) and iface.node is element.node and iface.has_internet:
280 element.external_iface = iface
283 # Set standard TUN attributes
284 if (not element.tun_addr or not element.tun_port) and element.external_iface:
285 element.tun_addr = element.external_iface.address
286 element.tun_port = 15000 + int(guid)
289 traces = testbed_instance._get_traces(guid)
290 element.capture = 'packets' in traces
292 # Do some validations
296 if element.peer_proto:
297 if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface):
299 listening = id(element) < id(element.peer_iface)
302 if not element.tun_addr or not element.tun_port:
304 elif not element.peer_addr or not element.peer_port:
307 # both have addresses...
308 # ...the one with the lesser address listens
309 listening = element.tun_addr < element.peer_addr
314 def postconfigure_tuniface(testbed_instance, guid):
315 element = testbed_instance._elements[guid]
321 def configure_node(testbed_instance, guid):
322 node = testbed_instance._elements[guid]
324 # Just inject configuration stuff
325 node.home_path = "nepi-node-%s" % (guid,)
326 node.ident_path = testbed_instance.sliceSSHKey
327 node.slicename = testbed_instance.slicename
329 # Do some validations
332 # recently provisioned nodes may not be up yet
334 while not node.is_alive():
335 time.sleep(sleeptime)
336 sleeptime = min(30.0, sleeptime*1.5)
338 # this will be done in parallel in all nodes
339 # this call only spawns the process
340 node.install_dependencies()
342 def configure_application(testbed_instance, guid):
343 app = testbed_instance._elements[guid]
345 # Do some validations
348 # Wait for dependencies
349 app.node.wait_dependencies()
354 def configure_dependency(testbed_instance, guid):
355 dep = testbed_instance._elements[guid]
357 # Do some validations
360 # Wait for dependencies
361 dep.node.wait_dependencies()
366 def configure_netpipe(testbed_instance, guid):
367 netpipe = testbed_instance._elements[guid]
369 # Do some validations
372 # Wait for dependencies
373 netpipe.node.wait_dependencies()
378 ### Factory information ###
380 connector_types = dict({
382 "help": "Connector from node to applications",
388 "help": "Connector from node to network interfaces",
394 "help": "Connector from node to application dependencies "
395 "(packages and applications that need to be installed)",
401 "help": "Connector from network interfaces to the internet",
407 "help": "Connector to a Node",
413 "help": "Connector to a NetPipe",
420 "help": "ip-ip tunneling over TCP link",
426 "help": "ip-ip tunneling over UDP datagrams",
435 "from": (TESTBED_ID, NODE, "devs"),
436 "to": (TESTBED_ID, NODEIFACE, "node"),
437 "init_code": connect_node_iface_node,
441 "from": (TESTBED_ID, NODE, "devs"),
442 "to": (TESTBED_ID, TUNIFACE, "node"),
443 "init_code": connect_tun_iface_node,
447 "from": (TESTBED_ID, NODEIFACE, "inet"),
448 "to": (TESTBED_ID, INTERNET, "devs"),
449 "init_code": connect_node_iface_inet,
453 "from": (TESTBED_ID, NODE, "apps"),
454 "to": (TESTBED_ID, APPLICATION, "node"),
455 "init_code": connect_dep,
459 "from": (TESTBED_ID, NODE, "deps"),
460 "to": (TESTBED_ID, DEPENDENCY, "node"),
461 "init_code": connect_dep,
465 "from": (TESTBED_ID, NODE, "deps"),
466 "to": (TESTBED_ID, NEPIDEPENDENCY, "node"),
467 "init_code": connect_dep,
471 "from": (TESTBED_ID, NODE, "deps"),
472 "to": (TESTBED_ID, NS3DEPENDENCY, "node"),
473 "init_code": connect_dep,
477 "from": (TESTBED_ID, NODE, "pipes"),
478 "to": (TESTBED_ID, NETPIPE, "node"),
479 "init_code": connect_node_netpipe,
483 "from": (TESTBED_ID, TUNIFACE, "tcp"),
484 "to": (TESTBED_ID, TUNIFACE, "tcp"),
485 "init_code": functools.partial(connect_tun_iface_peer,"tcp"),
489 "from": (TESTBED_ID, TUNIFACE, "udp"),
490 "to": (TESTBED_ID, TUNIFACE, "udp"),
491 "init_code": functools.partial(connect_tun_iface_peer,"udp"),
495 "from": (TESTBED_ID, TUNIFACE, "tcp"),
496 "to": (None, None, "tcp"),
497 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"),
498 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"),
502 "from": (TESTBED_ID, TUNIFACE, "udp"),
503 "to": (None, None, "udp"),
504 "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"),
505 "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"),
511 "forward_X11": dict({
512 "name": "forward_X11",
513 "help": "Forward x11 from main namespace to the node",
514 "type": Attribute.BOOL,
516 "flags": Attribute.DesignOnly,
517 "validation_function": validation.is_bool,
521 "help": "Constrain hostname during resource discovery. May use wildcards.",
522 "type": Attribute.STRING,
523 "flags": Attribute.DesignOnly,
524 "validation_function": validation.is_string,
526 "architecture": dict({
527 "name": "architecture",
528 "help": "Constrain architexture during resource discovery.",
529 "type": Attribute.ENUM,
530 "flags": Attribute.DesignOnly,
531 "allowed": ["x86_64",
533 "validation_function": validation.is_enum,
535 "operating_system": dict({
536 "name": "operatingSystem",
537 "help": "Constrain operating system during resource discovery.",
538 "type": Attribute.ENUM,
539 "flags": Attribute.DesignOnly,
545 "validation_function": validation.is_enum,
549 "help": "Constrain the PlanetLab site this node should reside on.",
550 "type": Attribute.ENUM,
551 "flags": Attribute.DesignOnly,
555 "validation_function": validation.is_enum,
559 "help": "Enable emulation on this node. Enables NetfilterRoutes, bridges, and a host of other functionality.",
560 "type": Attribute.BOOL,
562 "flags": Attribute.DesignOnly,
563 "validation_function": validation.is_bool,
565 "min_reliability": dict({
566 "name": "minReliability",
567 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
568 "type": Attribute.DOUBLE,
570 "flags": Attribute.DesignOnly,
571 "validation_function": validation.is_double,
573 "max_reliability": dict({
574 "name": "maxReliability",
575 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
576 "type": Attribute.DOUBLE,
578 "flags": Attribute.DesignOnly,
579 "validation_function": validation.is_double,
581 "min_bandwidth": dict({
582 "name": "minBandwidth",
583 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
584 "type": Attribute.DOUBLE,
586 "flags": Attribute.DesignOnly,
587 "validation_function": validation.is_double,
589 "max_bandwidth": dict({
590 "name": "maxBandwidth",
591 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
592 "type": Attribute.DOUBLE,
594 "flags": Attribute.DesignOnly,
595 "validation_function": validation.is_double,
601 "type": Attribute.BOOL,
603 "validation_function": validation.is_bool
607 "help": "This is the primary interface for the attached node",
608 "type": Attribute.BOOL,
610 "validation_function": validation.is_bool
612 "device_name": dict({
614 "help": "Device name",
615 "type": Attribute.STRING,
616 "flags": Attribute.DesignOnly,
617 "validation_function": validation.is_string
621 "help": "Maximum transmition unit for device",
622 "type": Attribute.INTEGER,
624 "validation_function": validation.is_integer_range(0,1500)
628 "help": "Network mask for the device (eg: 24 for /24 network)",
629 "type": Attribute.INTEGER,
630 "validation_function": validation.is_integer_range(8,24)
634 "help": "Enable SNAT (source NAT to the internet) no this device",
635 "type": Attribute.BOOL,
637 "validation_function": validation.is_bool
641 "help": "Transmission queue length (in packets)",
642 "type": Attribute.INTEGER,
643 "flags": Attribute.DesignOnly,
645 "validation_function": validation.is_integer
650 "help": "Command line string",
651 "type": Attribute.STRING,
652 "flags": Attribute.DesignOnly,
653 "validation_function": validation.is_string
657 "help": "Run with root privileges",
658 "type": Attribute.BOOL,
659 "flags": Attribute.DesignOnly,
661 "validation_function": validation.is_bool
665 "help": "Standard input",
666 "type": Attribute.STRING,
667 "flags": Attribute.DesignOnly,
668 "validation_function": validation.is_string
673 "help": "Space-separated list of packages required to run the application",
674 "type": Attribute.STRING,
675 "flags": Attribute.DesignOnly,
676 "validation_function": validation.is_string
678 "build-depends": dict({
679 "name": "buildDepends",
680 "help": "Space-separated list of packages required to build the application",
681 "type": Attribute.STRING,
682 "flags": Attribute.DesignOnly,
683 "validation_function": validation.is_string
687 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
688 "Archives won't be expanded automatically.",
689 "type": Attribute.STRING,
690 "flags": Attribute.DesignOnly,
691 "validation_function": validation.is_string
695 "help": "Build commands to execute after deploying the sources. "
696 "Sources will be in the ${SOURCES} folder. "
697 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
698 "Try to make the commands return with a nonzero exit code on error.\n"
699 "Also, do not install any programs here, use the 'install' attribute. This will "
700 "help keep the built files constrained to the build folder (which may "
701 "not be the home folder), and will result in faster deployment. Also, "
702 "make sure to clean up temporary files, to reduce bandwidth usage between "
703 "nodes when transferring built packages.",
704 "type": Attribute.STRING,
705 "flags": Attribute.DesignOnly,
706 "validation_function": validation.is_string
710 "help": "Commands to transfer built files to their final destinations. "
711 "Sources will be in the initial working folder, and a special "
712 "tag ${SOURCES} can be used to reference the experiment's "
713 "home folder (where the application commands will run).\n"
714 "ALL sources and targets needed for execution must be copied there, "
715 "if building has been enabled.\n"
716 "That is, 'slave' nodes will not automatically get any source files. "
717 "'slave' nodes don't get build dependencies either, so if you need "
718 "make and other tools to install, be sure to provide them as "
719 "actual dependencies instead.",
720 "type": Attribute.STRING,
721 "flags": Attribute.DesignOnly,
722 "validation_function": validation.is_string
724 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP: dict({
725 "name": ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
726 "help": "Commands to set up the environment needed to run NEPI testbeds",
727 "type": Attribute.STRING,
728 "flags": Attribute.Invisible | Attribute.ReadOnly,
729 "validation_function": validation.is_string
732 "netpipe_mode": dict({
734 "help": "Link mode:\n"
735 " * SERVER: applies to incoming connections\n"
736 " * CLIENT: applies to outgoing connections\n"
737 " * SERVICE: applies to both",
738 "type": Attribute.ENUM,
739 "flags": Attribute.DesignOnly,
740 "allowed": ["SERVER",
743 "validation_function": validation.is_enum,
747 "help": "Port list or range. Eg: '22', '22,23,27', '20-2000'",
748 "type": Attribute.STRING,
749 "validation_function": is_portlist,
753 "help": "Address list or range. Eg: '127.0.0.1', '127.0.0.1,127.0.1.1', '127.0.0.1/8'",
754 "type": Attribute.STRING,
755 "validation_function": is_addrlist,
759 "help": "Inbound bandwidth limit (in Mbit/s)",
760 "type": Attribute.DOUBLE,
761 "validation_function": validation.is_double,
765 "help": "Outbound bandwidth limit (in Mbit/s)",
766 "type": Attribute.DOUBLE,
767 "validation_function": validation.is_double,
771 "help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
772 "type": Attribute.DOUBLE,
773 "validation_function": validation.is_double,
777 "help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
778 "type": Attribute.DOUBLE,
779 "validation_function": validation.is_double,
783 "help": "Inbound packet delay (in milliseconds)",
784 "type": Attribute.INTEGER,
786 "validation_function": validation.is_integer,
790 "help": "Outbound packet delay (in milliseconds)",
791 "type": Attribute.INTEGER,
793 "validation_function": validation.is_integer,
800 "help": "Standard output stream"
804 "help": "Application standard error",
808 "help": "Output of the build process",
811 "netpipe_stats": dict({
812 "name": "netpipeStats",
813 "help": "Information about rule match counters, packets dropped, etc.",
818 "help": "Detailled log of all packets going through the interface",
822 create_order = [ INTERNET, NODE, NODEIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
824 configure_order = [ INTERNET, NODE, NODEIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
826 factories_info = dict({
828 "allow_routes": False,
829 "help": "Virtualized Node (V-Server style)",
830 "category": "topology",
831 "create_function": create_node,
832 "preconfigure_function": configure_node,
845 # NEPI-in-NEPI attributes
846 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
848 "connector_types": ["devs", "apps", "pipes", "deps"]
851 "has_addresses": True,
852 "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.",
853 "category": "devices",
854 "create_function": create_nodeiface,
855 "preconfigure_function": configure_nodeiface,
856 "box_attributes": [ ],
857 "connector_types": ["node", "inet"]
860 "allow_addresses": True,
861 "help": "Virtual TUN network interface",
862 "category": "devices",
863 "create_function": create_tuniface,
864 "preconfigure_function": preconfigure_tuniface,
865 "configure_function": postconfigure_tuniface,
867 "up", "device_name", "mtu", "snat",
869 "tun_proto", "tun_addr", "tun_port"
871 "traces": ["packets"],
872 "connector_types": ["node","udp","tcp"]
875 "help": "Generic executable command line application",
876 "category": "applications",
877 "create_function": create_application,
878 "start_function": start_application,
879 "status_function": status_application,
880 "stop_function": stop_application,
881 "configure_function": configure_application,
882 "box_attributes": ["command", "sudo", "stdin",
883 "depends", "build-depends", "build", "install",
885 "connector_types": ["node"],
886 "traces": ["stdout", "stderr", "buildlog"]
889 "help": "Requirement for package or application to be installed on some node",
890 "category": "applications",
891 "create_function": create_dependency,
892 "configure_function": configure_dependency,
893 "box_attributes": ["depends", "build-depends", "build", "install",
895 "connector_types": ["node"],
896 "traces": ["buildlog"]
898 NEPIDEPENDENCY: dict({
899 "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node",
900 "category": "applications",
901 "create_function": create_nepi_dependency,
902 "configure_function": configure_dependency,
903 "box_attributes": [ ],
904 "connector_types": ["node"],
905 "traces": ["buildlog"]
907 NS3DEPENDENCY: dict({
908 "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.",
909 "category": "applications",
910 "create_function": create_ns3_dependency,
911 "configure_function": configure_dependency,
912 "box_attributes": [ ],
913 "connector_types": ["node"],
914 "traces": ["buildlog"]
917 "help": "Internet routing",
918 "category": "topology",
919 "create_function": create_internet,
920 "connector_types": ["devs"],
923 "help": "Link emulation",
924 "category": "topology",
925 "create_function": create_netpipe,
926 "configure_function": configure_netpipe,
927 "box_attributes": ["netpipe_mode",
928 "addr_list", "port_list",
929 "bw_in","plr_in","delay_in",
930 "bw_out","plr_out","delay_out"],
931 "connector_types": ["node"],
932 "traces": ["netpipe_stats"]
936 testbed_attributes = dict({
939 "help": "The name of the PlanetLab slice to use",
940 "type": Attribute.STRING,
941 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
942 "validation_function": validation.is_string
946 "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
947 "type": Attribute.STRING,
948 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
949 "validation_function": validation.is_string
953 "help": "The PlanetLab user's password.",
954 "type": Attribute.STRING,
955 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
956 "validation_function": validation.is_string
958 "slice_ssh_key": dict({
959 "name": "sliceSSHKey",
960 "help": "The controller-local path to the slice user's ssh private key. "
961 "It is the user's responsability to deploy this file where the controller "
962 "will run, it won't be done automatically because it's sensitive information. "
963 "It is recommended that a NEPI-specific user be created for this purpose and "
964 "this purpose alone.",
965 "type": Attribute.STRING,
966 "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
967 "validation_function": validation.is_string
971 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
973 def connector_types(self):
974 return connector_types
977 def connections(self):
981 def attributes(self):
989 def create_order(self):
993 def configure_order(self):
994 return configure_order
997 def factories_info(self):
998 return factories_info
1001 def testbed_attributes(self):
1002 return testbed_attributes