Slight design change: separate build from install of applications.
authorClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Tue, 26 Apr 2011 12:22:57 +0000 (14:22 +0200)
committerClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Tue, 26 Apr 2011 12:22:57 +0000 (14:22 +0200)
This will enable easier and more efficient mass-deployment of identical applications
using the spanning tree method.

src/nepi/testbeds/planetlab/application.py
src/nepi/testbeds/planetlab/metadata_v01.py
test/testbeds/planetlab/execute.py

index e4ff075..bcc8eb6 100644 (file)
@@ -25,6 +25,7 @@ class Application(object):
         self.sudo = False
         
         self.build = None
+        self.install = None
         self.depends = None
         self.buildDepends = None
         self.sources = None
@@ -228,6 +229,16 @@ class Application(object):
             if proc.wait():
                 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
 
+    def _replace_paths(self, command):
+        """
+        Replace all special path tags with shell-escaped actual paths.
+        """
+        # need to append ${HOME} if paths aren't absolute, to MAKE them absolute.
+        root = '' if self.home_path.startswith('/') else "${HOME}/"
+        return ( command
+            .replace("${SOURCES}", root+server.shell_escape(self.home_path))
+            .replace("${BUILD}", root+server.shell_escape(os.path.join(self.home_path,'build'))) )
+
     def _build(self):
         if self.sources:
             sources = self.sources.split(' ')
@@ -264,10 +275,10 @@ class Application(object):
         
             
         if self.build:
-            # Install build dependencies
+            # Build sources
             (out,err),proc = server.popen_ssh_command(
-                "cd %(home)s ; %(command)s" % {
-                    'command' : self.build,
+                "cd %(home)s && mkdir -p build && cd build && %(command)s" % {
+                    'command' : self._replace_paths(self.build),
                     'home' : server.shell_escape(self.home_path),
                 },
                 host = self.node.hostname,
@@ -281,4 +292,38 @@ class Application(object):
             if proc.wait():
                 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
 
+            # Make archive
+            (out,err),proc = server.popen_ssh_command(
+                "cd %(home)s && tar czf build.tar.gz build" % {
+                    'command' : self._replace_paths(self.build),
+                    'home' : server.shell_escape(self.home_path),
+                },
+                host = self.node.hostname,
+                port = None,
+                user = self.slicename,
+                agent = None,
+                ident_key = self.ident_path,
+                server_key = self.node.server_key
+                )
+        
+            if proc.wait():
+                raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
+
+        if self.install:
+            # Install application
+            (out,err),proc = server.popen_ssh_command(
+                "cd %(home)s && cd build && %(command)s" % {
+                    'command' : self._replace_paths(self.install),
+                    'home' : server.shell_escape(self.home_path),
+                },
+                host = self.node.hostname,
+                port = None,
+                user = self.slicename,
+                agent = None,
+                ident_key = self.ident_path,
+                server_key = self.node.server_key
+                )
+        
+            if proc.wait():
+                raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
 
index 2d6b7f0..78a9831 100644 (file)
@@ -408,9 +408,27 @@ attributes = dict({
     "build": dict({
                 "name": "build",
                 "help": "Build commands to execute after deploying the sources. "
-                        "Sources will be in the initial working folder. "
-                        "Example: cd my-app && ./configure && make && make install.\n"
-                        "Try to make the commands return with a nonzero exit code on error.",
+                        "Sources will be in the ${SOURCES} folder. "
+                        "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
+                        "Try to make the commands return with a nonzero exit code on error.\n"
+                        "Also, do not install any programs here, use the 'install' attribute. This will "
+                        "help keep the built files constrained to the build folder (which may "
+                        "not be the home folder), and will result in faster deployment. Also, "
+                        "make sure to clean up temporary files, to reduce bandwidth usage between "
+                        "nodes when transferring built packages.",
+                "type": Attribute.STRING,
+                "flags": Attribute.DesignOnly,
+                "validation_function": validation.is_string
+            }),
+    "install": dict({
+                "name": "install",
+                "help": "Commands to transfer built files to their final destinations. "
+                        "Sources will be in the initial working folder, and a special "
+                        "tag ${SOURCES} can be used to reference the experiment's "
+                        "home folder (where the application commands will run).\n"
+                        "ALL sources and targets needed for execution must be copied there, "
+                        "if building has been enabled.\n"
+                        "That is, 'slave' nodes will not automatically get any source files.",
                 "type": Attribute.STRING,
                 "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_string
@@ -486,7 +504,7 @@ factories_info = dict({
             "stop_function": stop_application,
             "configure_function": configure_application,
             "box_attributes": ["command", "sudo", "stdin",
-                               "depends", "build-depends", "build",
+                               "depends", "build-depends", "build", "install",
                                "sources" ],
             "connector_types": ["node"],
             "traces": ["stdout", "stderr"]
index 2eb91cd..f20c127 100755 (executable)
@@ -126,7 +126,8 @@ class PlanetLabExecuteTestCase(unittest.TestCase):
         instance.defer_create(10, "Application")
         instance.defer_create_set(10, "command", "./consts")
         instance.defer_create_set(10, "buildDepends", "gcc")
-        instance.defer_create_set(10, "build", "gcc consts.c -o consts")
+        instance.defer_create_set(10, "build", "gcc ${SOURCES}/consts.c -o consts")
+        instance.defer_create_set(10, "install", "cp consts ${SOURCES}/consts")
         instance.defer_create_set(10, "sources", os.path.join(os.path.dirname(planetlab.__file__),'scripts','consts.c'))
         instance.defer_add_trace(10, "stdout")
         instance.defer_connect(10, "node", 2, "apps")