node.upload knows how to optionnally create executable files
[nepi.git] / nepi / resources / linux / node.py
index 375ff3a..3043857 100644 (file)
@@ -25,6 +25,7 @@ from nepi.util.sshfuncs import ProcStatus
 
 import collections
 import os
+import stat
 import random
 import re
 import tempfile
@@ -149,81 +150,74 @@ class LinuxNode(ResourceManager):
 
     @classmethod
     def _register_attributes(cls):
-        cls._register_attribute(Attribute(
-            "hostname", "Hostname of the machine",
-            flags = Flags.Design))
-
-        cls._register_attribute(Attribute(
-            "username", "Local account username", 
-            flags = Flags.Credential))
-
-        cls._register_attribute(Attribute(
-            "port", "SSH port",
-            flags = Flags.Design))
-        
-        cls._register_attribute(Attribute(
-            "home",
-            "Experiment home directory to store all experiment related files",
-            flags = Flags.Design))
-        
-        cls._register_attribute(Attribute(
-            "identity", "SSH identity file",
-            flags = Flags.Credential))
-        
-        cls._register_attribute(Attribute(
-            "serverKey", "Server public key", 
-            flags = Flags.Design))
-        
-        cls._register_attribute(Attribute(
-            "cleanHome",
-            "Remove all nepi files and directories "
-            " from node home folder before starting experiment", 
-            type = Types.Bool,
-            default = False,
-            flags = Flags.Design))
-
-        cls._register_attribute(Attribute(
-            "cleanExperiment", "Remove all files and directories " 
-            " from a previous same experiment, before the new experiment starts", 
-            type = Types.Bool,
-            default = False,
-            flags = Flags.Design))
-        
-        cls._register_attribute(Attribute(
-            "cleanProcesses", 
-            "Kill all running processes before starting experiment",
-            type = Types.Bool,
-            default = False,
-            flags = Flags.Design))
-        
-        cls._register_attribute(Attribute(
-            "cleanProcessesAfter", 
-            """Kill all running processes after starting experiment
-            This might be dangerous when using user root""",
-            type = Types.Bool,
-            default = True,
-            flags = Flags.Design))
-        
-        cls._register_attribute(Attribute(
-            "tearDown",
-            "Bash script to be executed before releasing the resource",
-            flags = Flags.Design))
-
-        cls._register_attribute(Attribute(
-            "gatewayUser",
-            "Gateway account username",
-            flags = Flags.Design))
-
-        cls._register_attribute(Attribute(
-            "gateway",
-            "Hostname of the gateway machine",
-            flags = Flags.Design))
-
-        cls._register_attribute(Attribute(
-            "ip",
-            "Linux host public IP address. "
-            "Must not be modified by the user unless hostname is 'localhost'",
+        cls._register_attribute(
+            Attribute("hostname",
+                      "Hostname of the machine",
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("username",
+                      "Local account username", 
+                      flags = Flags.Credential))
+        cls._register_attribute(
+            Attribute("port",
+                      "SSH port",
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("home",
+                      "Experiment home directory to store all experiment related files",
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("identity",
+                      "SSH identity file",
+                      flags = Flags.Credential))
+        cls._register_attribute(
+            Attribute("serverKey",
+                      "Server public key", 
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("cleanHome",
+                      "Remove all nepi files and directories "
+                      " from node home folder before starting experiment", 
+                      type = Types.Bool,
+                      default = False,
             flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("cleanExperiment",
+                      "Remove all files and directories " 
+                      " from a previous same experiment, before the new experiment starts", 
+                      type = Types.Bool,
+                      default = False,
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("cleanProcesses",
+                      "Kill all running processes before starting experiment",
+                      type = Types.Bool,
+                      default = False,
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("cleanProcessesAfter",
+                      "Kill all running processes after starting experiment"
+                      "NOTE: This might be dangerous when using user root",
+                      type = Types.Bool,
+                      default = True,
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("tearDown",
+                      "Bash script to be executed before releasing the resource",
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("gatewayUser",
+                      "Gateway account username",
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("gateway",
+                      "Hostname of the gateway machine",
+                      flags = Flags.Design))
+        cls._register_attribute(
+            Attribute("ip",
+                      "Linux host public IP address. "
+                      "Must not be modified by the user unless hostname is 'localhost'",
+                      flags = Flags.Design))
 
     def __init__(self, ec, guid):
         super(LinuxNode, self).__init__(ec, guid)
@@ -749,7 +743,7 @@ class LinuxNode(ResourceManager):
         return (out, err), proc
 
     def upload(self, src, dst, text = False, overwrite = True,
-               raise_on_error = True):
+               raise_on_error = True, executable = False):
         """ Copy content to destination
 
         src  string with the content to copy. Can be:
@@ -761,8 +755,9 @@ class LinuxNode(ResourceManager):
         dst  string with destination path on the remote host (remote is 
             always self.host)
 
-        text src is text input, it must be stored into a temp file before 
-        uploading
+        when src is text input, it gets stored into a temp file before 
+        uploading; in this case, and if executable is True, said temp file
+        is made executable, and thus uploaded file will be too
         """
         # If source is a string input 
         f = None
@@ -774,6 +769,12 @@ class LinuxNode(ResourceManager):
             f = tempfile.NamedTemporaryFile(mode=mode, delete=False)
             f.write(src)
             f.close()
+            if executable:
+                # do something like chmod u+x
+                mode = os.stat(f.name).st_mode
+                mode |= stat.S_IXUSR
+                os.chmod(f.name, mode)
+                
             src = f.name
 
         # If dst files should not be overwritten, check that the files do not
@@ -1189,7 +1190,10 @@ class LinuxNode(ResourceManager):
 
         (out, err), proc = self.execute(command, retry = 1, with_lock = True)
         
-        for d in dests:
+        # avoid RuntimeError that would result from
+        # changing loop subject during iteration
+        keys = list(dests.keys())
+        for d in keys:
             if out.find(d) > -1:
                 del dests[d]
 
@@ -1199,4 +1203,3 @@ class LinuxNode(ResourceManager):
         retcod = dests.values()
         if PY3: retcod = list(retcod)
         return retcod
-