multicast_overlay experoment
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Mon, 3 Oct 2011 20:51:51 +0000 (22:51 +0200)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Mon, 3 Oct 2011 20:51:51 +0000 (22:51 +0200)
examples/multicast_overlay.py
src/nepi/core/design.py

index 7372f92..4eb7ea2 100644 (file)
@@ -19,17 +19,18 @@ import operator
 import ipaddr
 import gzip
 import random
+import math
 
 class PlanetLabMulticastOverlay:
     testbed_id = "planetlab"
-    slicename = "inria_nepi"
+    slicename = "inria_nepi12"
     plchost = "www.planet-lab.eu"
     plkey = os.environ.get(
             "PL_SSH_KEY",
             "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
     pluser = os.environ.get("PL_USER")
     plpass = os.environ.get("PL_PASS")
-    vnet = "192.168.3.0"
+    vnet = "192.168.2.0"
     
     port_base = 2000 + (os.getpid() % 1000) * 13
     
@@ -68,17 +69,16 @@ class PlanetLabMulticastOverlay:
         pl_desc.set_attribute_value("plLogLevel", "INFO")
    
         netns_provider = FactoriesProvider("netns")
-        netns_desc = exp_desc.add_testbed_description(netns_provider)
-        netns_desc.set_attribute_value("homeDirectory", self.root_dir)
-        netns_desc.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
+        netns = exp_desc.add_testbed_description(netns_provider)
+        netns.set_attribute_value("homeDirectory", self.root_dir)
+        netns.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
         netns_root_dir = os.path.join(self.root_dir, "netns")
         os.mkdir(netns_root_dir)
-        netns_desc.set_attribute_value(DC.ROOT_DIRECTORY, netns_root_dir)
-        netns_desc.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
-        netns_desc.set_attribute_value(DC.USE_SUDO, True)
+        netns.set_attribute_value(DC.ROOT_DIRECTORY, netns_root_dir)
+        netns.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
+        netns.set_attribute_value(DC.USE_SUDO, True)
 
-        return pl_desc, netns_desc, exp_desc
-    
+        return pl_desc, netns, exp_desc
 
     def make_pl_tapnode(self, pl, ip, inet = None, label = None, hostname = None, routes = None, mcast = False, mcastrouter = False):
         if not isinstance(ip, list):
@@ -102,11 +102,8 @@ class PlanetLabMulticastOverlay:
             if label:
                 _tap1.set_attribute_value("label", label+"tap"+(str(i+1) if i else ""))
         
-            _tap1ip = _tap1.add_address()
-            _tap1ip.set_attribute_value("Address", ip)
-            _tap1ip.set_attribute_value("NetPrefix", 32)
-            _tap1ip.set_attribute_value("Broadcast", False)
-        
+            _tap1ip = self.add_ip_address(_tap1, ip, 32)
+            
             node1.connector("devs").connect(_tap1.connector("node"))
             
             tap1.append(_tap1)
@@ -117,10 +114,7 @@ class PlanetLabMulticastOverlay:
         iface1.connector("inet").connect(inet.connector("devs"))
         
         for destip, destprefix, nexthop in routes:
-            r1 = node1.add_route()
-            r1.set_attribute_value("Destination", destip)
-            r1.set_attribute_value("NetPrefix", destprefix)
-            r1.set_attribute_value("NextHop", nexthop)
+            r1 = self.add_route(node1, destip, destprefix, nexthop)
         
         if mcast:
             fwd = pl.create("MulticastForwarder")
@@ -132,7 +126,21 @@ class PlanetLabMulticastOverlay:
                 mrt.enable_trace("stderr")
                 
         return node1, iface1, tap1, tap1ip, inet
-    
+
+    def add_ip_address(self, iface, address, netprefix, broadcast = False):
+        ip = iface.add_address()
+        ip.set_attribute_value("Address", address)
+        ip.set_attribute_value("NetPrefix", netprefix)
+        ip.set_attribute_value("Broadcast", broadcast)
+        return ip
+
+    def add_route(self, node, destination, netprefix, nexthop):
+        route = node.add_route()
+        route.set_attribute_value("Destination", destination)
+        route.set_attribute_value("NetPrefix", netprefix)
+        route.set_attribute_value("NextHop", nexthop)
+        return route
+
     def add_vlc_base(self, pl, node):
         app = pl.create("Application")
         app.set_attribute_value("rpmFusion", True)
@@ -164,8 +172,8 @@ class PlanetLabMulticastOverlay:
         return app
     
     def add_vlc_source(self, netns, node, iflabel):
-        app = netns_desc.create("Application")
-        app.set_attribute_value("user", os.getlogin())
+        app = netns.create("Application")
+        app.set_attribute_value("user", self.user)
         app.set_attribute_value("label","vlc_source_%d" % (node.guid,))
         app.set_attribute_value("command",
             "vlc -vvv -I dummy "
@@ -195,68 +203,258 @@ class PlanetLabMulticastOverlay:
 
         # Create NS3 testbed running in node1
         ns3_provider = FactoriesProvider(ns3_testbed_id)
-        ns3_desc = exp.add_testbed_description(ns3_provider)
-        ns3_desc.set_attribute_value("rootDirectory", root)
-        ns3_desc.set_attribute_value("SimulatorImplementationType", "ns3::RealtimeSimulatorImpl")
-        ns3_desc.set_attribute_value("ChecksumEnabled", True)
-        ns3_desc.set_attribute_value(DC.DEPLOYMENT_HOST, "{#[%s].addr[0].[Address]#}" % (
+        ns = exp.add_testbed_description(ns3_provider)
+        ns.set_attribute_value("rootDirectory", root)
+        ns.set_attribute_value("SimulatorImplementationType", "ns3::RealtimeSimulatorImpl")
+        ns.set_attribute_value("ChecksumEnabled", True)
+        ns.set_attribute_value(DC.DEPLOYMENT_HOST, "{#[%s].addr[0].[Address]#}" % (
             iface1.get_attribute_value("label"),))
-        ns3_desc.set_attribute_value(DC.DEPLOYMENT_USER, 
+        ns.set_attribute_value(DC.DEPLOYMENT_USER, 
             pl.get_attribute_value("slice"))
-        ns3_desc.set_attribute_value(DC.DEPLOYMENT_KEY, 
+        ns.set_attribute_value(DC.DEPLOYMENT_KEY, 
             pl.get_attribute_value("sliceSSHKey"))
-        ns3_desc.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
-        ns3_desc.set_attribute_value(DC.DEPLOYMENT_COMMUNICATION, DC.ACCESS_SSH)
-        ns3_desc.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP,
+        ns.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
+        ns.set_attribute_value(DC.DEPLOYMENT_COMMUNICATION, DC.ACCESS_SSH)
+        ns.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP,
             "{#[%s].[%s]#}" % (
                 node1.get_attribute_value("label"),
                 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,))
-        ns3_desc.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
+        ns.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
         
-        return ns3_desc
-    
-    def make_netns_node(self, netns_desc):
-        node = netns_desc.create("Node")
+        return ns
+  
+    def add_pl_ns_node(self, pl_desc, inet, label_prefix):
+        node = pl_desc.create("Node")
+        node.set_attribute_value("label", label_prefix)
+        iface = pl_desc.create("NodeInterface")
+        iface.set_attribute_value("label", label_prefix+"iface")
+        iface.connector("inet").connect(inet.connector("devs"))
+        node.connector("devs").connect(iface.connector("node"))
+        forwarder = pl_desc.create("MulticastForwarder")
+        forwarder.enable_trace("stderr")
+        node.connector("apps").connect(forwarder.connector("node"))
+        return node, iface
+
+    def add_pl_ns_connection(self, pl_desc, pl_node, pl_addr,
+            ns, ns_node, ns_addr):
+        pl_tap = pl_desc.create("TapInterface")
+        pl_tap.set_attribute_value("tun_cipher", "PLAIN") 
+        self.add_ip_address(pl_tap, pl_addr, 31)
+        pl_node.connector("devs").connect(pl_tap.connector("node"))
+        ns_fdnd = ns.create("ns3::FdNetDevice")
+        ns_node.connector("devs").connect(ns_fdnd.connector("node"))
+        self.add_ip_address(ns_fdnd, ns_addr, 31)
+        pl_tap.connector("fd->").connect(ns_fdnd.connector("->fd"))
+
+    def add_pl_ns_tunchan_connection(self, pl_desc, pl_node, pl_addr,
+            ns, ns_node, ns_addr):
+        pl_tap = pl_desc.create("TunInterface")
+        self.add_ip_address(pl_tap, pl_addr, 31)
+        pl_node.connector("devs").connect(pl_tap.connector("node"))
+        ns_fdnd = ns.create("ns3::FdNetDevice")
+        ns_fdnd.enable_trace("FdPcapTrace")
+        self.add_ip_address(ns_fdnd, ns_addr, 31)
+        ns_node.connector("devs").connect(ns_fdnd.connector("node"))
+        ns_tc = ns.create("ns3::Nepi::TunChannel")
+        ns_tc.connector("fd->").connect(ns_fdnd.connector("->fd"))
+        pl_tap.connector("tcp").connect(ns_tc.connector("tcp"))
+
+    def make_netns_node(self, netns):
+        node = netns.create("Node")
         node.set_attribute_value("forward_X11", True)
+        command = "xterm" 
+        app = netns.create("Application")
+        app.set_attribute_value("command", command)
+        app.set_attribute_value("user", self.user)
+        app.connector("node").connect(node.connector("apps"))
         return node
 
-    def make_pl_netns_connection(self, pl_desc, pl_node, netns_desc,
-            netns_node, netns_iface_label, vnet):
-        base = struct.unpack('!L',socket.inet_aton(vnet))[0]
+    def make_pl_netns_connection(self, pl_desc, pl_node, netns,
+            netns_node, netns_iface_label):
+        base=struct.unpack('!L',socket.inet_aton(self.vnet))[0]
         netns_addr = socket.inet_ntoa(struct.pack('!L',(base | 1)))
         pl_addr = socket.inet_ntoa(struct.pack('!L',(base | 2)))
-
         pl_tap = pl_desc.create("TunInterface")
         pl_tap.set_attribute_value("multicast", True) 
         #pl_tap.set_attribute_value("tun_cipher", "PLAIN") 
         #pl_tap.enable_trace("pcap")
         #pl_tap.enable_trace("packets")
-        addr = pl_tap.add_address()
-        adrr.set_attribute_value("Address", pl_addr)
-        addr.set_attribute_value("NetPrefix", 32)
-        addr.set_attribute_value("Broadcast", False)
+        self.add_ip_address(pl_tap, pl_addr, 31)
         pl_node.connector("devs").connect(pl_tap.connector("node"))
         
-        netns_tap = netns_desc.create("TunNodeInterface")
+        netns_tap = netns.create("TunNodeInterface")
         netns_tap.set_attribute_value("label", netns_iface_label)
         netns_tap.set_attribute_value("up", True)
         netns_tap.set_attribute_value("mtu", 1448)
-        addr = netns_tap.add_address()
-        adrr.set_attribute_value("Address", netns_addr)
-        addr.set_attribute_value("NetPrefix", 32)
-        addr.set_attribute_value("Broadcast", False)
-        route = netns_node.add_route()
-        route.set_attribute_value("Destination", vnet)
-        r1.set_attribute_value("NetPrefix", 24)
-        r1.set_attribute_value("NextHop", pl_addr)
+        self.add_ip_address(netns_tap, netns_addr, 31)
+        self.add_route(netns_node, self.vnet, 24, pl_addr)
         netns_node.connector("devs").connect(netns_tap.connector("node"))
 
-        netns_tunchannel = netns_desc.create("TunChannel")
+        netns_tunchannel = netns.create("TunChannel")
         #netns_tunchannel.set_attribute_value("tun_cipher", "PLAIN") 
         netns_tunchannel.connector("->fd").connect(netns_tap.connector("fd->"))
         pl_tap.connector("tcp").connect(netns_tunchannel.connector("tcp"))
 
-    def make_pl_overlay(self, numnodes, num_wifi):
+    def add_ns_fdnd(self, ns, node):
+        fdnd = ns.create("ns3::FdNetDevice")
+        node.connector("devs").connect(fdnd.connector("node"))
+        #fdnd.enable_trace("FdPcapTrace")
+        return fdnd
+
+    def add_ns_node(self, ns):
+        node = ns.create("ns3::Node")
+        ipv4 = ns.create("ns3::Ipv4L3Protocol")
+        arp  = ns.create("ns3::ArpL3Protocol")
+        icmp = ns.create("ns3::Icmpv4L4Protocol")
+        udp = ns.create("ns3::UdpL4Protocol")
+        node.connector("protos").connect(ipv4.connector("node"))
+        node.connector("protos").connect(arp.connector("node"))
+        node.connector("protos").connect(icmp.connector("node"))
+        node.connector("protos").connect(udp.connector("node"))
+        return node
+
+    def add_ns_wifi_dev(self, ns, node, access_point = False):
+        wifi = ns.create("ns3::WifiNetDevice")
+        node.connector("devs").connect(wifi.connector("node"))
+
+        phy = ns.create("ns3::YansWifiPhy")
+        error = ns.create("ns3::NistErrorRateModel")
+        manager = ns.create("ns3::ArfWifiManager")
+        if access_point:
+            mac = ns.create("ns3::ApWifiMac")
+        else:
+            mac = ns.create("ns3::StaWifiMac")
+
+        phy.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
+        mac.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
+        phy.connector("err").connect(error.connector("phy"))
+        wifi.connector("phy").connect(phy.connector("dev"))
+        wifi.connector("mac").connect(mac.connector("dev"))
+        wifi.connector("manager").connect(manager.connector("dev"))
+
+        #phy.enable_trace("YansWifiPhyPcapTrace")
+        return wifi, phy
+
+    def add_ns_constant_mobility(self, ns, node, x, y, z):
+        mobility = ns.create("ns3::ConstantPositionMobilityModel") 
+        position = "%d:%d:%d" % (x, y, z)
+        mobility.set_attribute_value("Position", position)
+        node.connector("mobility").connect(mobility.connector("node"))
+        return mobility
+
+    def add_ns_wifi_channel(self, ns):
+        channel = ns.create("ns3::YansWifiChannel")
+        delay = ns.create("ns3::ConstantSpeedPropagationDelayModel")
+        loss  = ns.create("ns3::LogDistancePropagationLossModel")
+        channel.connector("delay").connect(delay.connector("chan"))
+        channel.connector("loss").connect(loss.connector("prev"))
+        return channel
+
+    def make_ns_wifi(self, ns, pl, pl_ns_root, inet, numwifinodes, nextip): 
+        base=struct.unpack('!L',socket.inet_aton(self.vnet))[0]
+        error = False
+        for i in xrange(2, 6):
+            nr = int(math.pow(2, i))
+            if nr <= (numwifinodes + 2):
+                break
+        else:
+            error = True
+        
+        # how many IPs will we need?
+        # 1 for the AP, 2 for each station and one for each extra PL node
+        # BUT we need to also reserve IPs to sum up to a posible subnetwork
+        # number of nodes: 2, 4, 8, 16, etc ...
+        # And finally, we need 2 extra IPs for the PL-AP iface
+       
+        nrips = (1 + 2*numwifinodes + nr + 2)
+        if nrips + nextip[0] > 255:
+            error = True
+        if error:
+            raise RuntimeError("There are not enough IP addresses for the wireless network", )
+        
+        netprefix = 32 - i
+        _nextwifiip = [254]
+        def nextwifiip():
+            ip = socket.inet_ntoa(struct.pack('!L',(base | _nextwifiip[0])))
+            _nextwifiip[0] -= 1
+            return ip
+
+        _nextnstapip = [(254 - nr -1)]
+        def nextnstapip():
+            ip = socket.inet_ntoa(struct.pack('!L',(base | _nextnstapip[0])))
+            _nextnstapip[0] -= 1
+            return ip
+
+        _nexttapip = [(254 - nr - 1 - numwifinodes)]
+        def nexttapip():
+            ip = socket.inet_ntoa(struct.pack('!L',(base | _nexttapip[0])))
+            _nexttapip[0] -= 1
+            return ip
+
+        # WIFI network
+        wifi_chan = self.add_ns_wifi_channel(ns)
+        
+        # AP node
+        ap_node = self.add_ns_node(ns)
+        self.add_ns_constant_mobility(ns, ap_node, 0, 0, 0)
+        ap_wifi, ap_phy = self.add_ns_wifi_dev(ns, ap_node, access_point = True)
+        ap_phy.connector("chan").connect(wifi_chan.connector("phys"))
+
+        # connect AP to PL
+        _nextplip = (254 - nrips)
+        pl_ip = socket.inet_ntoa(struct.pack('!L',(base | _nextplip)))
+        print "PL IP %s" % pl_ip
+        _nextplip -= 1
+        ns_ip = socket.inet_ntoa(struct.pack('!L',(base | _nextplip)))
+        print "NS IP %s" % ns_ip
+        self.add_pl_ns_connection(pl, pl_ns_root, pl_ip, ns, ap_node, ns_ip)
+
+        # routes in and out ns
+        self.add_route(ap_node, self.vnet, 24, pl_ip)
+        net = 256 - nr
+        ip = socket.inet_ntoa(struct.pack('!L',(base | net)))
+        self.add_route(pl_ns_root, ip, netprefix, ns_ip)
+        
+        ap_ip = nextwifiip()
+        print "AP IP %s" % ap_ip
+        self.add_ip_address(ap_wifi, ap_ip, netprefix)
+        
+        r = 50
+        # STA nodes
+        for i in xrange(0, numwifinodes):
+            stai = self.add_ns_node(ns)
+            angi = (360/numwifinodes)*i
+            xi = r*math.cos(angi)
+            yi = r*math.sin(angi)
+            self.add_ns_constant_mobility(ns, stai, xi, yi, 0)
+            wifi, phy = self.add_ns_wifi_dev(ns, stai, access_point = False)
+            phy.connector("chan").connect(wifi_chan.connector("phys"))
+            
+            wifi_ip = nextwifiip()
+            print "WIFI IP %s" % wifi_ip
+            self.add_ip_address(wifi, wifi_ip, netprefix)
+            self.add_route(stai, self.vnet, 24, ap_ip)
+            
+            """
+            pl_nodei, pl_ifacei = self.add_pl_ns_node(pl, inet, 
+                    "node2%d_pl"%i)
+           
+            pl_addr = (self.base_addr%(net+1))
+            ns3_addr = (self.base_addr%(net+2))
+            self.add_pl_ns_tunchan_connection(pl_desc, pl_nodei, pl_addr,
+                ns, stai, ns3_addr)
+            self.add_route(pl_nodei, (self.base_addr%32), 27, ns3_addr)
+            self.add_route(pl_nodei, (self.base_addr%0), 30, ns3_addr)
+            self.add_route(pl_nodei, (self.base_addr%4), 30, ns3_addr)
+
+            network = (self.base_addr%net)
+            self.add_route(netns_node, network, 30, (self.base_addr%2))
+            self.add_route(pl_node1, network, 30, (self.base_addr%6))
+            self.add_route(ap_node, network, 30, wifi_addr)
+            """
+
+    def make_pl_overlay(self, numnodes, numwifinodes):
+        print "make_pl_overlay ..."
         ns3_testbed_id = "ns3"
         
         pl, netns, exp = self.make_experiment_desc()
@@ -299,6 +497,7 @@ class PlanetLabMulticastOverlay:
             if parent:
                 parent.childips.update(node.childips)
                 
+        print "traverse..."
         traverse(traverse, root)
         
         def printtree(printtree, node, indent=''):
@@ -311,9 +510,11 @@ class PlanetLabMulticastOverlay:
                     print indent, '|- R', cnet, '->', cip
                 printtree(printtree, child, indent+' | ')
         printtree(printtree, root)
-
-        inet = pl.create("Internet")
         
+        inet = pl.create("Internet")
+       
+        ns_chosen = []
+
         def maketree(maketree, node, parent=None, parentIp=None):
             routes = []
             ctaps = []
@@ -325,6 +526,7 @@ class PlanetLabMulticastOverlay:
                 for cnet in childnets:
                     routes.append((cnet.ip.exploded, cnet.prefixlen, cip.exploded))
                 ctaps.append( maketree(maketree, child, node, pip) )
+
             if parentIp:
                 routes.append((self.vnet,24,parentIp))
             
@@ -332,6 +534,9 @@ class PlanetLabMulticastOverlay:
                 label = "root"
             else:
                 label = None
+                if not ns_chosen and node.children:
+                    ns_chosen.append(True)
+                    label = "ns_root"
             ips = [ ipaddr.IPAddress(node.vif_addr+i) for i in xrange(1+len(node.children)) ]
             node1, iface1, tap1, tap1ip, _ = self.make_pl_tapnode(pl, ips, inet, 
                 hostname = node.hostname,
@@ -342,23 +547,31 @@ class PlanetLabMulticastOverlay:
             
             for tap, ctap in zip(tap1[1:], ctaps):
                 tap.connector("udp").connect(ctap.connector("udp"))
-            
+
             self.add_net_monitor(pl, node1)
             self.add_vlc_restreamer(pl, node1)
             if random.random() < 0.1 and parent:
                 self.add_vlc_dumper(pl, node1)
             
             return tap1[0]
+        
+        print "maketree..."
         maketree(maketree, root)
 
         # create a netns node and connect it to the root pl node
-        pl_root = exp_desc.get_element_by_label("root")
+        pl_root = exp.get_element_by_label("root")
         netns_source = self.make_netns_node(netns)
         iflabel = "source-iface"
-        self.make_pl_netns_connection(pl_desc, pl_root, netns_desc
-                netns_source, iflabel, self.vnet)
-        self.add_vlc_source(netns, netns_n, iflabel)
+        self.make_pl_netns_connection(pl, pl_root, netns
+                netns_source, iflabel)
+        self.add_vlc_source(netns, netns_source, iflabel)
  
+        # add ns wireless network 
+        pl_ns_root = exp.get_element_by_label("ns_root")
+        pl_ns_root_iface = exp.get_element_by_label("ns_rootiface")
+        ns = self.make_ns_in_pl(pl, exp, pl_ns_root, pl_ns_root_iface, "ns3")
+        self.make_ns_wifi(ns, pl, pl_ns_root, inet, numwifinodes, nextip)
+
         xml = exp.to_xml()
         test_dir = "./results"
 
@@ -425,7 +638,7 @@ class PlanetLabMulticastOverlay:
 if __name__ == '__main__':
     usage = "usage: %prog -n number_sta -m movie -u user"
     parser = OptionParser(usage=usage)
-    parser.add_option("-u", "--user", dest="user", help="Valid linux system user (not root).", type="str")
+    parser.add_option("-u", "--user", dest="user", help="Valid linux system user (not root).", type="str", default=os.getlogin())
     parser.add_option("-m", "--movie", dest="movie", help="Path to movie file to play", type="str")
     parser.add_option("-n", "--nsta", dest="nsta", help="Number of wifi stations", type="int")
     parser.add_option("-N", "--nodes", dest="nsta", help="Number of overlay nodes", type="int")
@@ -441,9 +654,10 @@ if __name__ == '__main__':
 
     exp = PlanetLabMulticastOverlay()
     exp.movie_source = options.movie
+    exp.user = options.user
     try:
         exp.setUp()
-        exp.make_pl_overlay(50, 8)
+        exp.make_pl_overlay(5, 2)
     finally:
         exp.tearDown()
 
index f40b54b..11b9d7b 100644 (file)
@@ -371,7 +371,7 @@ class ExperimentDescription(object):
             for box in tbd_desc.boxes:
                 l = box.get_attribute_value("label")
                 if label == l:
-                    return tbd_desc
+                    return box
         return None
     
     def add_testbed_description(self, provider, guid = None):