1 from neco.execution.attribute import Attribute, Flags
2 from neco.execution.resource import ResourceManager, clsinit, ResourceState
3 from neco.resources.linux.ssh_api import SSHApiFactory
8 class LinuxApplication(ResourceManager):
9 _rtype = "LinuxApplication"
12 def _register_attributes(cls):
13 command = Attribute("command", "Command to execute",
14 flags = Flags.ReadOnly)
15 forward_x11 = Attribute("forwardX11", " Enables X11 forwarding for SSH connections",
16 flags = Flags.ReadOnly)
17 env = Attribute("env", "Environment variables string for command execution",
18 flags = Flags.ReadOnly)
19 sudo = Attribute("sudo", "Run with root privileges",
20 flags = Flags.ReadOnly)
21 depends = Attribute("depends",
22 "Space-separated list of packages required to run the application",
23 flags = Flags.ReadOnly)
24 sources = Attribute("sources",
25 "Space-separated list of regular files to be deployed in the working "
26 "path prior to building. Archives won't be expanded automatically.",
27 flags = Flags.ReadOnly)
28 build = Attribute("build",
29 "Build commands to execute after deploying the sources. "
30 "Sources will be in the ${SOURCES} folder. "
31 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
32 "Try to make the commands return with a nonzero exit code on error.\n"
33 "Also, do not install any programs here, use the 'install' attribute. This will "
34 "help keep the built files constrained to the build folder (which may "
35 "not be the home folder), and will result in faster deployment. Also, "
36 "make sure to clean up temporary files, to reduce bandwidth usage between "
37 "nodes when transferring built packages.",
38 flags = Flags.ReadOnly)
39 install = Attribute("install",
40 "Commands to transfer built files to their final destinations. "
41 "Sources will be in the initial working folder, and a special "
42 "tag ${SOURCES} can be used to reference the experiment's "
43 "home folder (where the application commands will run).\n"
44 "ALL sources and targets needed for execution must be copied there, "
45 "if building has been enabled.\n"
46 "That is, 'slave' nodes will not automatically get any source files. "
47 "'slave' nodes don't get build dependencies either, so if you need "
48 "make and other tools to install, be sure to provide them as "
49 "actual dependencies instead.",
50 flags = Flags.ReadOnly)
51 stdin = Attribute("stdin", "Standard input", flags = Flags.ReadOnly)
52 stdout = Attribute("stdout", "Standard output", flags = Flags.ReadOnly)
53 stderr = Attribute("stderr", "Standard error", flags = Flags.ReadOnly)
55 tear_down = Attribute("tearDown", "Bash script to be executed before
56 releasing the resource", flags = Flags.ReadOnly)
58 cls._register_attribute(command)
59 cls._register_attribute(forward_x11)
60 cls._register_attribute(env)
61 cls._register_attribute(sudo)
62 cls._register_attribute(depends)
63 cls._register_attribute(sources)
64 cls._register_attribute(build)
65 cls._register_attribute(install)
66 cls._register_attribute(stdin)
67 cls._register_attribute(stdout)
68 cls._register_attribute(stderr)
69 cls._register_attribute(tear_down)
71 def __init__(self, ec, guid):
72 super(LinuxApplication, self).__init__(ec, guid)
75 self._home = "app-%s" % self.box.guid
78 self._logger = logging.getLogger("neco.linux.Application.%d" % guid)
86 return self._home # + node home
96 def provision(self, filters = None):
97 # verify home hash or clean home
105 # Wait until node is associated and deployed
110 dst = os.path.join(self.home, "app.sh")
112 # Create shell script with the command
113 # This way, complex commands and scripts can be ran seamlessly
116 env = self.get("env")
118 for envkey, envvals in env.iteritems():
119 for envval in envvals:
120 cmd += 'export %s=%s\n' % (envkey, envval)
122 cmd += self.get("command")
123 self.api.upload(cmd, dst)
125 command = 'bash ./app.sh'
126 stdin = 'stdin' if self.get("stdin") else None
127 self.api.run(command, self.home, stdin = stdin)
128 self._pid, self._ppid = self.api.checkpid(self.app_home)
132 self._state = ResourceState.STOPPED
135 tear_down = self.get("tearDown")
137 self.api.execute(tear_down)
139 return self.api.kill(self.pid, self.ppid)
142 return self.api.status(self.pid, self.ppid)
144 def make_app_home(self):
145 self.api.mkdir(self.home)
147 stdin = self.get("stdin")
149 self.api.upload(stdin, os.path.join(self.home, 'stdin'))
151 def _validate_connection(self, guid):
154 # XXX: What if it is connected to more than one node?
155 resources = self.find_resources(exact_tags = [tags.NODE])
156 self._node = resources[0] if len(resources) == 1 else None