X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fnepi%2Ftestbeds%2Fplanetlab%2Fmetadata_v01.py;h=85ae09935a68fb657b78115f5ca421a06712a41e;hb=684c8f0f3d2d3e6ff58897a2fca102c963d482c7;hp=27d9679610e971f894814eeb6dbaadce1fa1f8fe;hpb=d7de4f2de28e4ef0f2ea3d3a09d22fda79db65b3;p=nepi.git diff --git a/src/nepi/testbeds/planetlab/metadata_v01.py b/src/nepi/testbeds/planetlab/metadata_v01.py index 27d96796..85ae0993 100644 --- a/src/nepi/testbeds/planetlab/metadata_v01.py +++ b/src/nepi/testbeds/planetlab/metadata_v01.py @@ -17,6 +17,7 @@ import os.path NODE = "Node" NODEIFACE = "NodeInterface" TUNIFACE = "TunInterface" +TAPIFACE = "TapInterface" APPLICATION = "Application" DEPENDENCY = "Dependency" NEPIDEPENDENCY = "NepiDependency" @@ -42,13 +43,13 @@ def is_addrlist(attribute, value): if '/' in component: addr, mask = component.split('/',1) else: - addr, mask = component, 32 + addr, mask = component, '32' if mask is not None and not (mask and mask.isdigit()): # No empty or nonnumeric masks return False - if not validation.is_ip4_address(attribute, value): + if not validation.is_ip4_address(attribute, addr): # Address part must be ipv4 return False @@ -95,6 +96,7 @@ def connect_tun_iface_node(testbed_instance, node_guid, iface_guid): raise RuntimeError, "Use of TUN interfaces requires emulation" iface.node = node node.required_vsys.update(('fd_tuntap', 'vif_up')) + node.required_packages.update(('python', 'python-crypto', 'python-setuptools', 'gcc')) def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid): iface = testbed_instance._elements[iface_guid] @@ -102,6 +104,31 @@ def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid) iface.peer_iface = peer_iface iface.peer_proto = \ iface.tun_proto = proto + iface.tun_key = peer_iface.tun_key + +def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data): + iface = testbed_instance._elements[iface_guid] + iface.peer_iface = None + iface.peer_addr = peer_iface_data.get("tun_addr") + iface.peer_proto = peer_iface_data.get("tun_proto") or proto + iface.peer_port = peer_iface_data.get("tun_port") + iface.tun_key = min(iface.tun_key, peer_iface_data.get("tun_key")) + iface.tun_proto = proto + + preconfigure_tuniface(testbed_instance, iface_guid) + +def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data): + # refresh (refreshable) attributes for second-phase + iface = testbed_instance._elements[iface_guid] + iface.peer_addr = peer_iface_data.get("tun_addr") + iface.peer_proto = peer_iface_data.get("tun_proto") or proto + iface.peer_port = peer_iface_data.get("tun_port") + + postconfigure_tuniface(testbed_instance, iface_guid) + +def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_iface_data): + crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data) + crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data) def connect_dep(testbed_instance, node_guid, app_guid): node = testbed_instance._elements[node_guid] @@ -139,12 +166,17 @@ def create_node(testbed_instance, guid): # add constraint on number of (real) interfaces # by counting connected devices - dev_guids = testbed_instance.get_connected(guid, "node", "devs") + dev_guids = testbed_instance.get_connected(guid, "devs", "node") num_open_ifaces = sum( # count True values NODEIFACE == testbed_instance._get_factory_id(guid) for guid in dev_guids ) element.min_num_external_ifaces = num_open_ifaces + # require vroute vsys if we have routes to set up + routes = testbed_instance._add_route.get(guid) + if routes: + element.required_vsys.add("vroute") + testbed_instance.elements[guid] = element def create_nodeiface(testbed_instance, guid): @@ -155,6 +187,29 @@ def create_nodeiface(testbed_instance, guid): def create_tuniface(testbed_instance, guid): parameters = testbed_instance._get_parameters(guid) element = testbed_instance._make_tun_iface(parameters) + + # Set custom addresses, if there are any already + # Setting this early helps set up P2P links + if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix): + addresses = testbed_instance._add_address[guid] + for address in addresses: + (address, netprefix, broadcast) = address + element.add_address(address, netprefix, broadcast) + + testbed_instance.elements[guid] = element + +def create_tapiface(testbed_instance, guid): + parameters = testbed_instance._get_parameters(guid) + element = testbed_instance._make_tap_iface(parameters) + + # Set custom addresses, if there are any already + # Setting this early helps set up P2P links + if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix): + addresses = testbed_instance._add_address[guid] + for address in addresses: + (address, netprefix, broadcast) = address + element.add_address(address, netprefix, broadcast) + testbed_instance.elements[guid] = element def create_application(testbed_instance, guid): @@ -254,8 +309,8 @@ def configure_nodeiface(testbed_instance, guid): def preconfigure_tuniface(testbed_instance, guid): element = testbed_instance._elements[guid] - # Set custom addresses if any - if guid in testbed_instance._add_address: + # Set custom addresses if any, and if not set already + if guid in testbed_instance._add_address and not (element.address or element.netmask or element.netprefix): addresses = testbed_instance._add_address[guid] for address in addresses: (address, netprefix, broadcast) = address @@ -268,8 +323,9 @@ def preconfigure_tuniface(testbed_instance, guid): break # Set standard TUN attributes - element.tun_addr = element.external_iface.address - element.tun_port = 15000 + int(guid) + if (not element.tun_addr or not element.tun_port) and element.external_iface: + element.tun_addr = element.external_iface.address + element.tun_port = 15000 + int(guid) # Set enabled traces traces = testbed_instance._get_traces(guid) @@ -279,9 +335,23 @@ def preconfigure_tuniface(testbed_instance, guid): element.validate() # First-phase setup - element.prepare( - 'tun-%s' % (guid,), - id(element) < id(element.peer_iface) ) + if element.peer_proto: + if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface): + # intra tun + listening = id(element) < id(element.peer_iface) + else: + # cross tun + if not element.tun_addr or not element.tun_port: + listening = True + elif not element.peer_addr or not element.peer_port: + listening = True + else: + # both have addresses... + # ...the one with the lesser address listens + listening = element.tun_addr < element.peer_addr + element.prepare( + 'tun-%s' % (guid,), + listening) def postconfigure_tuniface(testbed_instance, guid): element = testbed_instance._elements[guid] @@ -289,6 +359,12 @@ def postconfigure_tuniface(testbed_instance, guid): # Second-phase setup element.setup() +def wait_tuniface(testbed_instance, guid): + element = testbed_instance._elements[guid] + + # Second-phase setup + element.async_launch_wait() + def configure_node(testbed_instance, guid): node = testbed_instance._elements[guid] @@ -311,6 +387,18 @@ def configure_node(testbed_instance, guid): # this call only spawns the process node.install_dependencies() +def configure_node_routes(testbed_instance, guid): + node = testbed_instance._elements[guid] + routes = testbed_instance._add_route.get(guid) + + if routes: + devs = [ dev + for dev_guid in testbed_instance.get_connected(guid, "devs", "node") + for dev in ( testbed_instance._elements.get(dev_guid) ,) + if dev and isinstance(dev, testbed_instance._interfaces.TunIface) ] + + node.configure_routes(routes, devs) + def configure_application(testbed_instance, guid): app = testbed_instance._elements[guid] @@ -321,7 +409,7 @@ def configure_application(testbed_instance, guid): app.node.wait_dependencies() # Install stuff - app.setup() + app.async_setup() def configure_dependency(testbed_instance, guid): dep = testbed_instance._elements[guid] @@ -333,7 +421,7 @@ def configure_dependency(testbed_instance, guid): dep.node.wait_dependencies() # Install stuff - dep.setup() + dep.async_setup() def configure_netpipe(testbed_instance, guid): netpipe = testbed_instance._elements[guid] @@ -400,6 +488,12 @@ connector_types = dict({ "max": 1, "min": 0 }), + "fd->": dict({ + "help": "TUN device file descriptor provider", + "name": "fd->", + "max": 1, + "min": 0 + }), }) connections = [ @@ -415,6 +509,12 @@ connections = [ "init_code": connect_tun_iface_node, "can_cross": False }), + dict({ + "from": (TESTBED_ID, NODE, "devs"), + "to": (TESTBED_ID, TAPIFACE, "node"), + "init_code": connect_tun_iface_node, + "can_cross": False + }), dict({ "from": (TESTBED_ID, NODEIFACE, "inet"), "to": (TESTBED_ID, INTERNET, "devs"), @@ -463,6 +563,58 @@ connections = [ "init_code": functools.partial(connect_tun_iface_peer,"udp"), "can_cross": False }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "tcp"), + "to": (TESTBED_ID, TAPIFACE, "tcp"), + "init_code": functools.partial(connect_tun_iface_peer,"tcp"), + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "udp"), + "to": (TESTBED_ID, TAPIFACE, "udp"), + "init_code": functools.partial(connect_tun_iface_peer,"udp"), + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, TUNIFACE, "tcp"), + "to": (None, None, "tcp"), + "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"), + "can_cross": True + }), + dict({ + "from": (TESTBED_ID, TUNIFACE, "udp"), + "to": (None, None, "udp"), + "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"), + "can_cross": True + }), + dict({ + "from": (TESTBED_ID, TUNIFACE, "fd->"), + "to": (None, None, "->fd"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"), + "can_cross": True + }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "tcp"), + "to": (None, None, "tcp"), + "init_code": functools.partial(crossconnect_tun_iface_peer_init,"tcp"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"tcp"), + "can_cross": True + }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "udp"), + "to": (None, None, "udp"), + "init_code": functools.partial(crossconnect_tun_iface_peer_init,"udp"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"), + "can_cross": True + }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "fd->"), + "to": (None, None, "->fd"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"), + "can_cross": True + }), ] attributes = dict({ @@ -594,6 +746,14 @@ attributes = dict({ "value": False, "validation_function": validation.is_bool }), + "pointopoint": dict({ + "name": "pointopoint", + "help": "If the interface is a P2P link, the remote endpoint's IP " + "should be set on this attribute.", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), "txqueuelen": dict({ "name": "mask", "help": "Transmission queue length (in packets)", @@ -679,13 +839,6 @@ attributes = dict({ "flags": Attribute.DesignOnly, "validation_function": validation.is_string }), - ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP: dict({ - "name": ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP, - "help": "Commands to set up the environment needed to run NEPI testbeds", - "type": Attribute.STRING, - "flags": Attribute.Invisible | Attribute.ReadOnly, - "validation_function": validation.is_string - }), "netpipe_mode": dict({ "name": "mode", @@ -777,17 +930,21 @@ traces = dict({ }), }) -create_order = [ INTERNET, NODE, NODEIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ] +create_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ] -configure_order = [ INTERNET, NODE, NODEIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ] +configure_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ] + +# Start (and prestart) node after ifaces, because the node needs the ifaces in order to set up routes +start_order = [ INTERNET, NODEIFACE, TAPIFACE, TUNIFACE, NODE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ] factories_info = dict({ NODE: dict({ - "allow_routes": False, + "allow_routes": True, "help": "Virtualized Node (V-Server style)", "category": "topology", "create_function": create_node, "preconfigure_function": configure_node, + "prestart_function": configure_node_routes, "box_attributes": [ "forward_X11", "hostname", @@ -816,17 +973,35 @@ factories_info = dict({ }), TUNIFACE: dict({ "allow_addresses": True, - "help": "Virtual TUN network interface", + "help": "Virtual TUN network interface (layer 3)", "category": "devices", "create_function": create_tuniface, "preconfigure_function": preconfigure_tuniface, "configure_function": postconfigure_tuniface, + "prestart_function": wait_tuniface, + "box_attributes": [ + "up", "device_name", "mtu", "snat", "pointopoint", + "txqueuelen", + "tun_proto", "tun_addr", "tun_port", "tun_key" + ], + "traces": ["packets"], + "connector_types": ["node","udp","tcp","fd->"] + }), + TAPIFACE: dict({ + "allow_addresses": True, + "help": "Virtual TAP network interface (layer 2)", + "category": "devices", + "create_function": create_tapiface, + "preconfigure_function": preconfigure_tuniface, + "configure_function": postconfigure_tuniface, + "prestart_function": wait_tuniface, "box_attributes": [ - "up", "device_name", "mtu", "snat", - "txqueuelen" + "up", "device_name", "mtu", "snat", "pointopoint", + "txqueuelen", + "tun_proto", "tun_addr", "tun_port", "tun_key" ], "traces": ["packets"], - "connector_types": ["node","udp","tcp"] + "connector_types": ["node","udp","tcp","fd->"] }), APPLICATION: dict({ "help": "Generic executable command line application", @@ -846,7 +1021,7 @@ factories_info = dict({ "help": "Requirement for package or application to be installed on some node", "category": "applications", "create_function": create_dependency, - "configure_function": configure_dependency, + "preconfigure_function": configure_dependency, "box_attributes": ["depends", "build-depends", "build", "install", "sources" ], "connector_types": ["node"], @@ -856,7 +1031,7 @@ factories_info = dict({ "help": "Requirement for NEPI inside NEPI - required to run testbed instances inside a node", "category": "applications", "create_function": create_nepi_dependency, - "configure_function": configure_dependency, + "preconfigure_function": configure_dependency, "box_attributes": [ ], "connector_types": ["node"], "traces": ["buildlog"] @@ -865,7 +1040,7 @@ factories_info = dict({ "help": "Requirement for NS3 inside NEPI - required to run NS3 testbed instances inside a node. It also needs NepiDependency.", "category": "applications", "create_function": create_ns3_dependency, - "configure_function": configure_dependency, + "preconfigure_function": configure_dependency, "box_attributes": [ ], "connector_types": ["node"], "traces": ["buildlog"] @@ -912,6 +1087,22 @@ testbed_attributes = dict({ "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, "validation_function": validation.is_string }), + "plc_host": dict({ + "name": "plcHost", + "help": "The PlanetLab PLC API host", + "type": Attribute.STRING, + "value": "www.planet-lab.eu", + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), + "plc_url": dict({ + "name": "plcUrl", + "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.", + "type": Attribute.STRING, + "value": "https://%(hostname)s:443/PLCAPI/", + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), "slice_ssh_key": dict({ "name": "sliceSSHKey", "help": "The controller-local path to the slice user's ssh private key. " @@ -950,6 +1141,14 @@ class VersionedMetadataInfo(metadata.VersionedMetadataInfo): def configure_order(self): return configure_order + @property + def prestart_order(self): + return start_order + + @property + def start_order(self): + return start_order + @property def factories_info(self): return factories_info