Fixing CCNx on PlanetLab to accept a user defined bind port.
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Thu, 10 May 2012 17:07:30 +0000 (19:07 +0200)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Thu, 10 May 2012 17:07:30 +0000 (19:07 +0200)
examples/ccnx/planetlab_ccnx_multicast.py
examples/ccnx/planetlab_ccnx_unicast.py
src/nepi/testbeds/planetlab/application.py
src/nepi/testbeds/planetlab/metadata.py

index cb8bf6b..cecb78f 100644 (file)
@@ -89,32 +89,45 @@ def create_tunnel(node, peer, pl_nodes, slice_desc, subnet):
     peerip.set_attribute_value("Address", peeraddr.exploded)
     peerip.set_attribute_value("NetPrefix", subnet.prefixlen)
 
-def create_ccnd(pl_node, hostname, routes, slice_desc):
+def create_ccnd(pl_node, port, hostname, routes, slice_desc):
     pl_app = slice_desc.create("CCNxDaemon")
     # We use a wildcard to replace the TUN IP address of the node during runtime
     routes = "|".join(map(lambda route: "udp 224.0.23.170 %d 3 1 {#[tun_%s%s].addr[0].[Address]#}" \
             % (route[1], hostname, route[0]), routes))
     # Add multicast ccn routes 
-    pl_app.set_attribute_value("ccnroutes", routes)
+    pl_app.set_attribute_value("ccnRoutes", routes)
+    # Use a specific port to bind the CCNx daemon
+    if port:
+        pl_app.set_attribute_value("ccnLocalPort", port)
     pl_app.enable_trace("stdout")
     pl_app.enable_trace("stderr")
     pl_app.connector("node").connect(pl_node.connector("apps"))
 
-def create_ccnsendchunks(pl_node, slice_desc):
+def create_ccnsendchunks(pl_node, port, slice_desc):
     pl_app = slice_desc.create("Application")
     path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
         "../big_buck_bunny_240p_mpeg4_lq.ts")
     pl_app.set_attribute_value("stdin", path_to_video)
-    pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
+
+    command = "ccnsendchunks ccnx:/VIDEO"
+    if port:
+        command = "CCN_LOCAL_PORT=%d %s " % (port, command)
+    pl_app.set_attribute_value("command", command)
+
     pl_app.enable_trace("stdout")
     pl_app.enable_trace("stderr")
     pl_app.connector("node").connect(pl_node.connector("apps"))
     return pl_app
 
-def exec_ccncatchunks(slicename, hostname):
+def exec_ccncatchunks(slicename, port, hostname):
     print "Starting Vlc streamming ..."
+
+    command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin;'
+    if port:
+        command += "CCN_LOCAL_PORT=%d " % port
+    command += ' ccncatchunks2 ccnx:/VIDEO'
+
     login = "%s@%s" % (slicename, hostname)
-    command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin; ccncatchunks2 ccnx:/VIDEO'
     proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
     proc2 = subprocess.Popen(['vlc', 
         '--sub-filter', 'marq', 
@@ -126,7 +139,7 @@ def exec_ccncatchunks(slicename, hostname):
     return proc2
 
 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
-        port_base, root_dir):
+        port_base, root_dir, port):
 
     # Create the experiment description object
     exp_desc = ExperimentDescription()
@@ -142,16 +155,16 @@ def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh
     pl_nodes = dict()
     ccn_routes = dict()
     prev_hostname = None
-    port = 49695
+    mport = port
     for hostname in hostnames:
         pl_node = create_node(hostname, pl_inet, slice_desc)
         pl_nodes[hostname] = pl_node
 
         ccn_routes[hostname] = list()
         if prev_hostname:
-            ccn_routes[hostname].append((prev_hostname, port))
-            ccn_routes[prev_hostname].append((hostname,  port))
-            port +=1
+            ccn_routes[hostname].append((prev_hostname, mport))
+            ccn_routes[prev_hostname].append((hostname, mport))
+            mport +=1
         prev_hostname = hostname
 
     # Get the base network segment (slice vsys_vnet) to assign all the IP addresses
@@ -180,21 +193,21 @@ def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh
     # Create ccnd daemons in all nodes
     for hostname, pl_node in pl_nodes.iteritems():
         routes = ccn_routes[hostname]
-        create_ccnd(pl_node, hostname, routes, slice_desc)
+        create_ccnd(pl_node, port, hostname, routes, slice_desc)
 
     # Create a ccnsendchunks application box in the first node
     hostname = hostnames[0]
     pl_node = pl_nodes[hostname]
-    pl_app = create_ccnsendchunks(pl_node, slice_desc)
+    pl_app = create_ccnsendchunks(pl_node, port, slice_desc)
 
     return exp_desc, pl_nodes, hostname, pl_app
 
 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
-        port_base, root_dir):
+        port_base, root_dir, port):
 
     exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet, 
             slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base, 
-            root_dir)
+            root_dir, port)
 
     xml = exp_desc.to_xml()
     controller = ExperimentController(xml, root_dir)
@@ -206,7 +219,7 @@ def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
     proc = None
     if not TERMINATE:
         hostname = hostnames[-1]
-        proc = exec_ccncatchunks(slicename, hostname)
+        proc = exec_ccncatchunks(slicename, port, hostname)
 
     while not TERMINATE and proc and proc.poll() is None:
         time.sleep(0.5)
@@ -249,8 +262,9 @@ if __name__ == '__main__':
                  'planetlabpc2.upf.edu',
                  'planet2.elte.hu',
                  'planetlab2.esprit-tn.com' ]
+    ccn_local_port = os.environ.get('CCN_LOCAL_PORT', 49695)
 
-    usage = "usage: %prog -s <pl_slice> -H <pl_host> -k <ssh_key> -u <pl_user> -p <pl_password> -v <vsys_vnet> -N <host_names> -c <node_count>"
+    usage = "usage: %prog -s <pl_slice> -H <pl_host> -k <ssh_key> -u <pl_user> -p <pl_password> -v <vsys_vnet> -N <host_names> -c <node_count> -P <ccn-local-port>"
 
     parser = OptionParser(usage=usage)
     parser.add_option("-s", "--slicename", dest="slicename", 
@@ -275,6 +289,10 @@ if __name__ == '__main__':
     parser.add_option("-c", "--node-count", dest="node_count", 
             help="Number of nodes to use", 
             default=5, type="str")
+    parser.add_option("-P", "--ccn-local-port", dest="port", 
+            help="Port to bind the CCNx daemon", 
+            default=ccn_local_port, type="int")
+
     (options, args) = parser.parse_args()
 
     hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
@@ -286,7 +304,8 @@ if __name__ == '__main__':
     pl_user= options.pl_user
     pl_pwd = options.pl_pwd
     pl_ssh_key = options.pl_ssh_key
+    port = options.port
 
     run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key, 
-            port_base, root_dir)
+            port_base, root_dir, port)
 
index 1d1e28d..7808081 100644 (file)
@@ -65,12 +65,11 @@ def create_node(hostname, pl_inet, slice_desc):
     pl_node.connector("devs").connect(pl_iface.connector("node"))
     return pl_node
 
-def create_ccnd(pl_node, routes, slice_desc):
+def create_ccnd(pl_node, port, routes, slice_desc):
     pl_app = slice_desc.create("CCNxDaemon")
     
     # We can specify a default ccnx version to be either ccnx-0.5.1 or ccnx-0.6.0
-    #pl_app.set_attribute_value("ccnxversion", "ccnx-0.5.1")
-    
+    #pl_app.set_attribute_value("ccnxVersion", "ccnx-0.5.1")
     # We can also specify a custom local source and build and install directives
     path_to_source = os.path.join(os.path.dirname(os.path.abspath(__file__)),
         "ccnx-0.6.0rc3.tar.gz")
@@ -80,42 +79,60 @@ def create_ccnd(pl_node, routes, slice_desc):
             "cd ./ccnx-0.6.0rc3 && "
             "./configure && make ")
     pl_app.set_attribute_value("install", "cp -r ./ccnx-0.6.0rc3/bin ${SOURCES}")
-    # We use a wildcard to replace the public IP address of the node during runtime
+
+    # We use a wildcard to replace the public IP address of the node during runtime,
+    # once this IP is known
     routes = "|".join(map(lambda route: "udp {#[iface_%s].addr[0].[Address]#}" % route, routes))
     
-    # Add multicast ccn routes 
-    pl_app.set_attribute_value("ccnroutes", routes)
+    # Add unicast ccn routes 
+    pl_app.set_attribute_value("ccnRoutes", routes)
+
+    # Use a specific port to bind the CCNx daemon
+    if port:
+        pl_app.set_attribute_value("ccnLocalPort", port)
+
     pl_app.enable_trace("stdout")
     pl_app.enable_trace("stderr")
     pl_app.connector("node").connect(pl_node.connector("apps"))
 
-def create_ccnsendchunks(pl_node, slice_desc):
+def create_ccnsendchunks(pl_node, port, slice_desc):
     pl_app = slice_desc.create("Application")
     path_to_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
         "../big_buck_bunny_240p_mpeg4_lq.ts")
     pl_app.set_attribute_value("stdin", path_to_video)
-    pl_app.set_attribute_value("command", "ccnsendchunks ccnx:/VIDEO")
+
+    command = "ccnsendchunks ccnx:/VIDEO"
+    if port:
+        command = "CCN_LOCAL_PORT=%d %s " % (port, command)
+    pl_app.set_attribute_value("command", command)
+
     pl_app.enable_trace("stdout")
     pl_app.enable_trace("stderr")
     pl_app.connector("node").connect(pl_node.connector("apps"))
     return pl_app
 
-def exec_ccncatchunks(slicename, hostname):
+def exec_ccncatchunks(slicename, port, hostname):
     print "Getting video chunks from %s ..." % hostname
+
+    command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin;'
+    if port:
+        command += "CCN_LOCAL_PORT=%d " % port
+    command += ' ccncatchunks2 ccnx:/VIDEO'
+
     login = "%s@%s" % (slicename, hostname)
-    command = 'PATH=$PATH:$(ls | egrep nepi-ccnd- | head -1)/bin; ccncatchunks2 ccnx:/VIDEO'
     proc1 = subprocess.Popen(['ssh', login, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = False)
+    
     proc2 = subprocess.Popen(['vlc', 
         '--sub-filter', 'marq', 
         '--marq-marquee', 
         '(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org', 
         '--marq-position=8', 
-        '--no-video-title-show',  '-'], 
+        '--no-video-title-show', '-'], 
         stdin=proc1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     return proc2
 
 def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
-        port_base, root_dir, delay):
+        port_base, root_dir, delay, port):
 
     # Create the experiment description object
     exp_desc = ExperimentDescription()
@@ -144,21 +161,21 @@ def create_ed(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh
     for hostname in hostnames:
         pl_node = pl_nodes[hostname] 
         routes = ccn_routes[hostname]
-        create_ccnd(pl_node, routes, slice_desc)
+        create_ccnd(pl_node, port, routes, slice_desc)
 
     # Create a ccnsendchunks application box in the first node
     hostname = hostnames[0]
     pl_node = pl_nodes[hostname]
-    pl_app = create_ccnsendchunks(pl_node, slice_desc)
+    pl_app = create_ccnsendchunks(pl_node, port, slice_desc)
 
     return exp_desc, pl_nodes, hostname, pl_app
 
 def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, 
-        port_base, root_dir, delay):
+        port_base, root_dir, delay, port):
 
     exp_desc, pl_nodes, hostname, pl_app = create_ed(hostnames, vsys_vnet, 
             slicename, plc_host, pl_user, pl_pwd, pl_ssh_key, port_base, 
-            root_dir, delay)
+            root_dir, delay, port)
 
     xml = exp_desc.to_xml()
     controller = ExperimentController(xml, root_dir)
@@ -170,7 +187,7 @@ def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
     proc1 = None
     if not TERMINATE:
         hostname = hostnames[-1]
-        proc1 = exec_ccncatchunks(slicename, hostname)
+        proc1 = exec_ccncatchunks(slicename, port, hostname)
 
     if not TERMINATE and proc1:
         time.sleep(delay)
@@ -178,7 +195,7 @@ def run(hostnames, vsys_vnet, slicename, plc_host, pl_user, pl_pwd, pl_ssh_key,
     proc2 = None
     if not TERMINATE:
         hostname = hostnames[-2]
-        proc2 = exec_ccncatchunks(slicename, hostname)
+        proc2 = exec_ccncatchunks(slicename, port, hostname)
 
     while not TERMINATE and proc1 and proc2 and proc2.poll() is None:
         time.sleep(0.5)
@@ -229,8 +246,9 @@ if __name__ == '__main__':
                  'planetlabpc2.upf.edu',
                  'planet2.elte.hu',
                  'planetlab2.esprit-tn.com' ]
+    ccn_local_port = os.environ.get('CCN_LOCAL_PORT')
 
-    usage = "usage: %prog -s <pl_slice> -H <pl_host> -k <ssh_key> -u <pl_user> -p <pl_password> -v <vsys_vnet> -N <host_names> -c <node_count> -d <delay>"
+    usage = "usage: %prog -s <pl_slice> -H <pl_host> -k <ssh_key> -u <pl_user> -p <pl_password> -v <vsys_vnet> -N <host_names> -c <node_count> -d <delay> -P <ccn-local-port>"
 
     parser = OptionParser(usage=usage)
     parser.add_option("-s", "--slicename", dest="slicename", 
@@ -258,6 +276,9 @@ if __name__ == '__main__':
     parser.add_option("-d", "--delay", dest="delay", 
             help="Time to wait before retrieveing the second video stream in seconds", 
             default=40, type="int")
+    parser.add_option("-P", "--ccn-local-port", dest="port", 
+            help="Port to bind the CCNx daemon", 
+            default=ccn_local_port, type="int")
     (options, args) = parser.parse_args()
 
     hostnames = map(string.strip, options.hostnames.split(",")) if options.hostnames else default_hostnames
@@ -270,7 +291,8 @@ if __name__ == '__main__':
     pl_pwd = options.pl_pwd
     pl_ssh_key = options.pl_ssh_key
     delay = options.delay
+    port = options.port
 
     run(hostnames, vsys_vnet, slicename, pl_host, pl_user, pl_pwd, pl_ssh_key, 
-            port_base, root_dir, delay)
+            port_base, root_dir, delay, port)
 
index da4748a..a192707 100644 (file)
@@ -19,6 +19,8 @@ import re
 
 from nepi.util.constants import ApplicationStatus as AS
 
+_ccnre = re.compile("\s*(udp|tcp)\s+(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\s*$")
+
 class Dependency(object):
     """
     A Dependency is in every respect like an application.
@@ -1105,9 +1107,9 @@ class CCNxDaemon(Application):
         super(CCNxDaemon,self).__init__(api)
         
         # Attributes
-        self.ccnroutes = None
-        self.ccnsources = None
-        self.ccnxversion = "ccnx-0.6.0"
+        self.ccnLocalPort = None
+        self.ccnRoutes = None
+        self.ccnxVersion = "ccnx-0.6.0"
         
         self.ccnx_0_5_1_sources = "http://www.ccnx.org/releases/ccnx-0.5.1.tar.gz"
         self.ccnx_0_6_0_sources = "http://www.ccnx.org/releases/ccnx-0.6.0.tar.gz"
@@ -1171,27 +1173,40 @@ class CCNxDaemon(Application):
     def setup(self):
         # setting ccn sources
         if not self.build:
-            if self.ccnxversion == 'ccnx-0.6.0':
+            if self.ccnxVersion == 'ccnx-0.6.0':
                 self.build = self.ccnx_0_6_0_build
-            elif self.ccnxversion == 'ccnx-0.5.1':
+            elif self.ccnxVersion == 'ccnx-0.5.1':
                 self.build = self.ccnx_0_5_1_build
 
         if not self.install:
-            if self.ccnxversion == 'ccnx-0.6.0':
+            if self.ccnxVersion == 'ccnx-0.6.0':
                 self.install = self.ccnx_0_6_0_install
-            elif self.ccnxversion == 'ccnx-0.5.1':
+            elif self.ccnxVersion == 'ccnx-0.5.1':
                 self.install = self.ccnx_0_5_1_install
 
         super(CCNxDaemon, self).setup()
 
     def start(self):
+        self.command = ""
+        if self.ccnLocalPort:
+            self.command = "export CCN_LOCAL_PORT=%s ; " % self.ccnLocalPort
+        self.command += " ccndstart "
+
         # configure ccn routes
-        routes = ""
-        if self.ccnroutes:
+        if self.ccnRoutes:
+            routes = self.ccnRoutes.split("|")
+            
+            if self.ccnLocalPort:
+                routes = map(lambda route: "%s %s" %(route, 
+                    self.ccnLocalPort) if _ccnre.match(route) else route, 
+                        routes)
+
             routes = map(lambda route: "ccndc add ccnx:/ %s" % route, 
-                self.ccnroutes.split("|"))
-            routes = "; " + " ; ".join(routes)
-        self.command = "ccndstart %s" % routes
+                routes)
+
+            routescmd = " ; ".join(routes)
+            self.command += " ; "
+            self.command += routescmd
 
         # Start will be invoked in prestart step
         super(CCNxDaemon, self).start()
@@ -1199,7 +1214,12 @@ class CCNxDaemon(Application):
     def kill(self):
         self._logger.info("Killing %s", self)
 
-        cmd = self._replace_paths("${SOURCES}/bin/ccndstop")
+        command = "${SOURCES}/bin/ccndstop"
+
+        if self.ccnLocalPort:
+            self.command = "export CCN_LOCAL_PORT=%s; %s" % (self.ccnLocalPort, command)
+
+        cmd = self._replace_paths(command)
         command = cStringIO.StringIO()
         command.write(cmd)
         command.seek(0)
index 39f506d..e69f3e7 100644 (file)
@@ -1127,7 +1127,7 @@ attributes = dict({
                 "validation_function": validation.is_string
             }),
     "ccnroutes": dict({
-                "name": "ccnroutes",
+                "name": "ccnRoutes",
                 "help": "Route can be static (e.g. udp ip) or multicast (e.g. udp 224.0.0.204 2869). To separate different route use '|' ",
                 "type": Attribute.STRING,
                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
@@ -1179,16 +1179,8 @@ attributes = dict({
                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
-    "ccnsources": dict({
-                "name": "ccnsources",
-                "help": "Path to local tar with ccnx sources."
-                        "Default source is http://www.ccnx.org/releases/ccnx-0.5.1.tar.gz",
-                "type": Attribute.STRING,
-                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
-                "validation_function": validation.is_string
-            }),
     "ccnxversion": dict({      
-                "name": "ccnxversion",
+                "name": "ccnxVersion",
                 "help": "Version of ccnx source code to install in the node.",
                 "type": Attribute.ENUM, 
                 "value": "ccnx-0.6.0",
@@ -1197,6 +1189,16 @@ attributes = dict({
                             "ccnx-0.5.1"],
                 "validation_function": validation.is_enum,
             }),
+     "ccnlocalport" : dict({
+            "name" : "ccnLocalPort", 
+            "help" : "Local port to bind the ccn daemon. (i.e. CCN_LOCAL_PORT=)",
+            "type" : Attribute.INTEGER,
+            "flags" : Attribute.DesignInvisible | \
+                    Attribute.ExecInvisible | \
+                    Attribute.ExecImmutable | \
+                    Attribute.Metadata,
+            "validation_function" : validation.is_integer,
+            }),
     "build": dict({
                 "name": "build",
                 "help": "Build commands to execute after deploying the sources. "
@@ -1583,7 +1585,7 @@ factories_info = dict({
             "status_function": status_application,
             "stop_function": stop_application,
             "configure_function": configure_application,
-            "box_attributes": ["ccnroutes", "ccnsources", "build", 
+            "box_attributes": ["ccnroutes", "build", "ccnlocalport",
                 "install", "ccnxversion", "sources"],
             "connector_types": ["node"],
             "traces": ["stdout", "stderr", "buildlog", "output"],