#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from nepi.core.description import AF_INET
+from constants import TESTBED_ID
+from nepi.core import metadata
from nepi.core.attributes import Attribute
from nepi.util import validation
+from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
+ STATUS_FINISHED
+
+NODE = "Node"
+P2PIFACE = "P2PNodeInterface"
+TAPIFACE = "TapNodeInterface"
+NODEIFACE = "NodeInterface"
+SWITCH = "Switch"
+APPLICATION = "Application"
+
+NS3_TESTBED_ID = "ns3"
+FDNETDEV = "ns3::FileDescriptorNetDevice"
+
+### Connection functions ####
+
+def connect_switch(testbed_instance, switch_guid, interface_guid):
+ switch = testbed_instance._elements[switch_guid]
+ interface = testbed_instance._elements[interface_guid]
+ switch.connect(interface)
+
+def connect_fd(testbed_instance, tap_guid, cross_data):
+ import passfd
+ import socket
+ tap = testbed_instance._elements[tap_guid]
+ fd = tap.file_descriptor
+ address = cross_data["LinuxSocketAddress"]
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ sock.connect(address)
+ passfd.sendfd(sock, fd, '0')
+ # TODO: after succesful transfer, the tap device should close the fd
+
+### Creation functions ###
+
+def create_node(testbed_instance, guid):
+ parameters = testbed_instance._get_parameters(guid)
+ forward_X11 = False
+ if "forward_X11" in parameters:
+ forward_X11 = parameters["forward_X11"]
+ del parameters["forward_X11"]
+ element = testbed_instance.netns.Node(forward_X11 = forward_X11)
+ testbed_instance.elements[guid] = element
+
+def create_p2piface(testbed_instance, guid):
+ if guid in testbed_instance.elements:
+ # The interface pair was already instantiated
+ return
+ # search for the node asociated with the p2piface
+ node1_guid = testbed_instance.get_connected(guid, "node", "devs")
+ if len(node1_guid) == 0:
+ raise RuntimeError("Can't instantiate interface %d outside netns \
+ node" % guid)
+ node1 = testbed_instance.elements[node1_guid[0]]
+ # search for the pair p2piface
+ p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
+ if len(p2p_guid) == 0:
+ raise RuntimeError("Can't instantiate p2p interface %d. \
+ Missing interface pair" % guid)
+ guid2 = p2p_guid[0]
+ node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
+ if len(node2_guid) == 0:
+ raise RuntimeError("Can't instantiate interface %d outside netns \
+ node" % guid2)
+ node2 = testbed_instance.elements[node2_guid[0]]
+ element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
+ node1, node2)
+ testbed_instance.elements[guid] = element1
+ testbed_instance.elements[guid2] = element2
+
+def create_tapiface(testbed_instance, guid):
+ node_guid = testbed_instance.get_connected(guid, "node", "devs")
+ if len(node_guid) == 0:
+ raise RuntimeError("Can't instantiate interface %d outside netns \
+ node" % guid)
+ node = testbed_instance.elements[node_guid[0]]
+ element = node.add_tap()
+ testbed_instance.elements[guid] = element
+
+def create_nodeiface(testbed_instance, guid):
+ node_guid = testbed_instance.get_connected(guid, "node", "devs")
+ if len(node_guid) == 0:
+ raise RuntimeError("Can't instantiate interface %d outside netns \
+ node" % guid)
+ node = testbed_instance.elements[node_guid[0]]
+ element = node.add_if()
+ testbed_instance.elements[guid] = element
+
+def create_switch(testbed_instance, guid):
+ element = testbed_instance.netns.Switch()
+ testbed_instance.elements[guid] = element
+
+def create_application(testbed_instance, guid):
+ testbed_instance.elements[guid] = None # Delayed construction
+
+### Start/Stop functions ###
+
+def start_application(testbed_instance, guid):
+ parameters = testbed_instance._get_parameters(guid)
+ traces = testbed_instance._get_traces(guid)
+ user = parameters["user"]
+ command = parameters["command"]
+ stdout = stderr = None
+ if "stdout" in traces:
+ filename = testbed_instance.trace_filename(guid, "stdout")
+ stdout = open(filename, "wb")
+ testbed_instance.follow_trace("stdout", stdout)
+ if "stderr" in traces:
+ filename = testbed_instance.trace_filename(guid, "stderr")
+ stderr = open(filename, "wb")
+ testbed_instance.follow_trace("stderr", stderr)
+
+ node_guid = testbed_instance.get_connected(guid, "node", "apps")
+ if len(node_guid) == 0:
+ raise RuntimeError("Can't instantiate interface %d outside netns \
+ node" % guid)
+ node = testbed_instance.elements[node_guid[0]]
+ element = node.Popen(command, shell = True, stdout = stdout,
+ stderr = stderr, user = user)
+ testbed_instance.elements[guid] = element
+
+### Status functions ###
+
+def status_application(testbed_instance, guid):
+ if guid not in testbed_instance.elements.keys():
+ return STATUS_NOT_STARTED
+ app = testbed_instance.elements[guid]
+ if app.poll() == None:
+ return STATUS_RUNNING
+ return STATUS_FINISHED
+
+### Configure functions ###
+
+def configure_device(testbed_instance, guid):
+ element = testbed_instance._elements[guid]
+ if not guid in testbed_instance._add_address:
+ return
+ addresses = testbed_instance._add_address[guid]
+ for address in addresses:
+ (address, netprefix, broadcast) = address
+ # TODO: Decide if we should add a ipv4 or ipv6 address
+ element.add_v4_address(address, netprefix)
+
+def configure_node(testbed_instance, guid):
+ element = testbed_instance._elements[guid]
+ if not guid in testbed_instance._add_route:
+ return
+ routes = testbed_instance._add_route[guid]
+ for route in routes:
+ (destination, netprefix, nexthop) = route
+ element.add_route(prefix = destination, prefix_len = netprefix,
+ nexthop = nexthop)
+
+### Factory information ###
+
+connector_types = dict({
+ "apps": dict({
+ "help": "Connector from node to applications",
+ "name": "apps",
+ "max": -1,
+ "min": 0
+ }),
+ "devs": dict({
+ "help": "Connector from node to network interfaces",
+ "name": "devs",
+ "max": -1,
+ "min": 0
+ }),
+ "node": dict({
+ "help": "Connector to a Node",
+ "name": "node",
+ "max": 1,
+ "min": 1
+ }),
+ "p2p": dict({
+ "help": "Connector to a P2PInterface",
+ "name": "p2p",
+ "max": 1,
+ "min": 0
+ }),
+ "fd": dict({
+ "help": "Connector to a network interface that can receive a file descriptor",
+ "name": "fd",
+ "max": 1,
+ "min": 0
+ }),
+ "switch": dict({
+ "help": "Connector to a switch",
+ "name": "switch",
+ "max": 1,
+ "min": 0
+ })
+ })
+
+connections = [
+ dict({
+ "from": (TESTBED_ID, NODE, "devs"),
+ "to": (TESTBED_ID, P2PIFACE, "node"),
+ "can_cross": False
+ }),
+ dict({
+ "from": (TESTBED_ID, NODE, "devs"),
+ "to": (TESTBED_ID, TAPIFACE, "node"),
+ "can_cross": False
+ }),
+ dict({
+ "from": (TESTBED_ID, NODE, "devs"),
+ "to": (TESTBED_ID, NODEIFACE, "node"),
+ "can_cross": False
+ }),
+ dict({
+ "from": (TESTBED_ID, P2PIFACE, "p2p"),
+ "to": (TESTBED_ID, P2PIFACE, "p2p"),
+ "can_cross": False
+ }),
+ dict({
+ "from": (TESTBED_ID, TAPIFACE, "fd"),
+ "to": (NS3_TESTBED_ID, FDNETDEV, "fd"),
+ "compl_code": connect_fd,
+ "can_cross": True
+ }),
+ dict({
+ "from": (TESTBED_ID, SWITCH, "devs"),
+ "to": (TESTBED_ID, NODEIFACE, "switch"),
+ "init_code": connect_switch,
+ "can_cross": False
+ }),
+ dict({
+ "from": (TESTBED_ID, NODE, "apps"),
+ "to": (TESTBED_ID, APPLICATION, "node"),
+ "can_cross": False
+ })
+]
+
+attributes = dict({
+ "forward_X11": dict({
+ "name": "forward_X11",
+ "help": "Forward x11 from main namespace to the node",
+ "type": Attribute.BOOL,
+ "value": False,
+ "flags": Attribute.DesignOnly,
+ "validation_function": validation.is_bool
+ }),
+ "lladdr": dict({
+ "name": "lladdr",
+ "help": "Mac address",
+ "type": Attribute.STRING,
+ "flags": Attribute.DesignOnly,
+ "validation_function": validation.is_mac_address
+ }),
+ "up": dict({
+ "name": "up",
+ "help": "Link up",
+ "type": Attribute.BOOL,
+ "value": False,
+ "validation_function": validation.is_bool
+ }),
+ "device_name": dict({
+ "name": "name",
+ "help": "Device name",
+ "type": Attribute.STRING,
+ "flags": Attribute.DesignOnly,
+ "validation_function": validation.is_string
+ }),
+ "mtu": dict({
+ "name": "mtu",
+ "help": "Maximum transmition unit for device",
+ "type": Attribute.INTEGER,
+ "validation_function": validation.is_integer
+ }),
+ "broadcast": dict({
+ "name": "broadcast",
+ "help": "Broadcast address",
+ "type": Attribute.STRING,
+ "validation_function": validation.is_string # TODO: should be is address!
+ }),
+ "multicast": dict({
+ "name": "multicast",
+ "help": "Multicast enabled",
+ "type": Attribute.BOOL,
+ "value": False,
+ "validation_function": validation.is_bool
+ }),
+ "arp": dict({
+ "name": "arp",
+ "help": "ARP enabled",
+ "type": Attribute.BOOL,
+ "value": False,
+ "validation_function": validation.is_bool
+ }),
+ "command": dict({
+ "name": "command",
+ "help": "Command line string",
+ "type": Attribute.STRING,
+ "flags": Attribute.DesignOnly,
+ "validation_function": validation.is_string
+ }),
+ "user": dict({
+ "name": "user",
+ "help": "System user",
+ "type": Attribute.STRING,
+ "flags": Attribute.DesignOnly,
+ "validation_function": validation.is_string
+ }),
+ "stdin": dict({
+ "name": "stdin",
+ "help": "Standard input",
+ "type": Attribute.STRING,
+ "flags": Attribute.DesignOnly,
+ "validation_function": validation.is_string
+ }),
+ })
+
+traces = dict({
+ "stdout": dict({
+ "name": "stdout",
+ "help": "Standard output stream"
+ }),
+ "stderr": dict({
+ "name": "stderr",
+ "help": "Application standard error",
+ })
+ })
+
+create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
+ APPLICATION ]
+
+configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE, SWITCH, NODE,
+ APPLICATION ]
+
+factories_info = dict({
+ NODE: dict({
+ "allow_routes": True,
+ "help": "Emulated Node with virtualized network stack",
+ "category": "topology",
+ "create_function": create_node,
+ "configure_function": configure_node,
+ "box_attributes": ["forward_X11"],
+ "connector_types": ["devs", "apps"]
+ }),
+ P2PIFACE: dict({
+ "allow_addresses": True,
+ "help": "Point to point network interface",
+ "category": "devices",
+ "create_function": create_p2piface,
+ "configure_function": configure_device,
+ "box_attributes": ["lladdr", "up", "device_name", "mtu",
+ "multicast", "broadcast", "arp"],
+ "connector_types": ["node", "p2p"]
+ }),
+ TAPIFACE: dict({
+ "allow_addresses": True,
+ "help": "Tap device network interface",
+ "category": "devices",
+ "create_function": create_tapiface,
+ "configure_function": configure_device,
+ "box_attributes": ["lladdr", "up", "device_name", "mtu",
+ "multicast", "broadcast", "arp"],
+ "connector_types": ["node", "fd"]
+ }),
+ NODEIFACE: dict({
+ "allow_addresses": True,
+ "help": "Node network interface",
+ "category": "devices",
+ "create_function": create_nodeiface,
+ "configure_function": configure_device,
+ "box_attributes": ["lladdr", "up", "device_name", "mtu",
+ "multicast", "broadcast", "arp"],
+ "connector_types": ["node", "switch"]
+ }),
+ SWITCH: dict({
+ "display_name": "Switch",
+ "help": "Switch interface",
+ "category": "devices",
+ "create_function": create_switch,
+ "box_attributes": ["up", "device_name", "mtu", "multicast"],
+ #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
+ #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
+ #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
+ #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
+ #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
+ "connector_types": ["devs"]
+ }),
+ APPLICATION: dict({
+ "help": "Generic executable command line application",
+ "category": "applications",
+ "create_function": create_application,
+ "start_function": start_application,
+ "status_function": status_application,
+ "box_attributes": ["command", "user"],
+ "connector_types": ["node"],
+ "traces": ["stdout", "stderr"]
+ }),
+})
+
+testbed_attributes = dict({
+ "enable_debug": dict({
+ "name": "enableDebug",
+ "help": "Enable netns debug output",
+ "type": Attribute.BOOL,
+ "value": False,
+ "validation_function": validation.is_bool
+ }),
+ })
+
+class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
+ @property
+ def connector_types(self):
+ return connector_types
+
+ @property
+ def connections(self):
+ return connections
+
+ @property
+ def attributes(self):
+ return attributes
+
+ @property
+ def traces(self):
+ return traces
+
+ @property
+ def create_order(self):
+ return create_order
+
+ @property
+ def configure_order(self):
+ return configure_order
+
+ @property
+ def factories_info(self):
+ return factories_info
+
+ @property
+ def testbed_attributes(self):
+ return testbed_attributes
-def get_metadata():
- return dict({
- "node": dict({
- "factory_type": "routing",
- "display_name": "Node",
- "help": "Node element",
- "category": "topology",
- "connector_types": [
- ("netns_node_apps",
- "Connector from node to applications",
- "apps", -1, 0,
- ["netns_application_node"]),
- ("netns_node_devs",
- "Connector from node to network interfaces",
- "devs", -1, 0,
- [
- "netns_nodeiface_node",
- "netns_tapiface_node",
- "netns_p2piface_node"
- ])
- ],
- "element_attributes": [
- ("forward_X11",
- "Forward x11 from main namespace to the node",
- Attribute.BOOL,
- False, None, None, False,
- validation.is_bool)
- ]
- }),
- "p2piface": dict({
- "factory_type": "addressable",
- "family": AF_INET,
- "max_addresses": 1,
- "display_name": "P2PInterface",
- "help": "Point to point network interface",
- "category": "devices",
- "connector_types": [
- ("netns_p2piface_node",
- "Connector from P2PInterface to Node",
- "node", 1, 1,
- ["netns_node_devs"]),
- ("netns_p2pinterface_p2p",
- "Connector to another P2PInterface",
- "p2p", 1, 0,
- ["netns_p2piface_p2p"])
- ],
- "element_attributes": [
- ("lladdr", "Mac address", Attribute.STRING,
- None, None, None, False,
- validation.is_mac_address),
- ("up", "Link up", Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("name", "Device name", Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("mtu", "Maximmum transmition unit for device",
- Attribute.INTEGER,
- None, None, None, False,
- validation.is_integer),
- ("broadcast", "Broadcast address",
- Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("multicast", "Is multicast enabled",
- Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("arp", "Is ARP enabled", Attribute.BOOL,
- True, None, None, False,
- validation.is_bool),
- ]
- }),
- "tapiface": dict({
- "factory_type": "addressable",
- "family": AF_INET,
- "max_addresses": 1,
- "display_name": "TapNodeInterface",
- "help": "Tap device network interface",
- "category": "devices",
- "connector_types": [
- ("netns_tapiface_node",
- "Connector to a Node",
- "node", 1, 1,
- ["netns_node_devs"]),
- ("netns_tapiface_fd",
- "Connector to a network interface that can receive a file descriptor",
- "fd", 1, 0,
- # TODO: Doesn't exist yet!
- ["ns3_fdnetdev_fd"])
- ],
- "element_attributes": [
- ("lladdr", "Mac address", Attribute.STRING,
- None, None, None, False,
- validation.is_mac_address),
- ("up", "Link up", Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("name", "Device name", Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("mtu", "Maximmum transmition unit for device",
- Attribute.INTEGER,
- None, None, None, False,
- validation.is_integer),
- ("broadcast", "Broadcast address",
- Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("multicast", "Is multicast enabled",
- Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("arp", "Is ARP enabled", Attribute.BOOL,
- True, None, None, False,
- validation.is_bool),
- ]
- }),
- "nodeiface": dict({ #TODO: Addresses!
- "factory_type": "addressable",
- "family": AF_INET,
- "max_addresses": 1,
- "display_name": "NodeInterface",
- "help": "Node network interface",
- "category": "devices",
- "connector_types": [
- ("netns_nodeiface_node", "Connector to a node",
- "node", 1, 1,
- ["netns_node_devs"]),
- ("netns_nodeiface_switch", "Connector to a switch",
- "switch", 1, 0,
- ["netns_switch_devs"])
- ],
- "element_attributes": [
- ("lladdr", "Mac address", Attribute.STRING,
- None, None, None, False,
- validation.is_mac_address),
- ("up", "Link up", Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("name", "Device name", Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("mtu", "Maximmum transmition unit for device",
- Attribute.INTEGER,
- None, None, None, False,
- validation.is_integer),
- ("broadcast", "Broadcast address",
- Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("multicast", "Is multicast enabled",
- Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("arp", "Is ARP enabled", Attribute.BOOL,
- True, None, None, False,
- validation.is_bool),
- ]
- }),
- "switch": dict({
- "display_name": "Switch",
- "help": "Switch interface",
- "category": "devices",
- "connector_types": [
- ("netns_switch_devs", "Connector to network interfaces",
- "devs", -1, 0,
- ["netns_nodeiface_switch"])
- ],
- "element_attributes": [
- ("up", "Link up", Attribute.BOOL,
- False, None, None, False,
- validation.is_bool),
- ("name", "Device name", Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("mtu", "Maximmum transmition unit for device",
- Attribute.INTEGER,
- None, None, None, False,
- validation.is_integer),
- ("multicast", "Is multicast enabled",
- Attribute.BOOL,
- None, None, None, False,
- validation.is_bool),
- #TODO:("Stp", help, type, value, range, allowed, readonly, validation_function),
- #TODO:("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
- #TODO:("HelloTime", help, type, value, range, allowed, readonly, validation_function),
- #TODO:("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
- #TODO:("MaxAge", help, type, value, range, allowed, readonly, validation_function)
- ]
- }),
- "application": dict({
- "display_name": "Application",
- "help": "Generic executable command line application",
- "category": "applications",
- "connector_types": [
- ("netns_application_node", "Connector to a node",
- "node", 1, 1,
- ["netns_node_apps"])
- ],
- "traces": [
- ("StdoutTrace", "Standard output"),
- ("StderrTrace", "Standard error")
- ],
- "element_attributes": [
- ("command", "Command line",
- Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("user", "System user",
- Attribute.STRING,
- None, None, None, False,
- validation.is_string),
- ("stdin", "Standard input",
- Attribute.STRING,
- None, None, None, False,
- validation.is_string)
- ]
- }),
- })