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 env = Attribute("env", "Environment variables string for command execution",
16 flags = Flags.ReadOnly)
17 sudo = Attribute("sudo", "Run with root privileges",
18 flags = Flags.ReadOnly)
19 depends = Attribute("depends",
20 "Space-separated list of packages required to run the application",
21 flags = Flags.ReadOnly)
22 sources = Attribute("sources",
23 "Space-separated list of regular files to be deployed in the working "
24 "path prior to building. Archives won't be expanded automatically.",
25 flags = Flags.ReadOnly)
26 build = Attribute("build",
27 "Build commands to execute after deploying the sources. "
28 "Sources will be in the ${SOURCES} folder. "
29 "Example: tar xzf ${SOURCES}/my-app.tgz && cd my-app && ./configure && make && make clean.\n"
30 "Try to make the commands return with a nonzero exit code on error.\n"
31 "Also, do not install any programs here, use the 'install' attribute. This will "
32 "help keep the built files constrained to the build folder (which may "
33 "not be the home folder), and will result in faster deployment. Also, "
34 "make sure to clean up temporary files, to reduce bandwidth usage between "
35 "nodes when transferring built packages.",
36 flags = Flags.ReadOnly)
37 install = Attribute("install",
38 "Commands to transfer built files to their final destinations. "
39 "Sources will be in the initial working folder, and a special "
40 "tag ${SOURCES} can be used to reference the experiment's "
41 "home folder (where the application commands will run).\n"
42 "ALL sources and targets needed for execution must be copied there, "
43 "if building has been enabled.\n"
44 "That is, 'slave' nodes will not automatically get any source files. "
45 "'slave' nodes don't get build dependencies either, so if you need "
46 "make and other tools to install, be sure to provide them as "
47 "actual dependencies instead.",
48 flags = Flags.ReadOnly)
49 stdin = Attribute("stdin", "Standard input", flags = Flags.ReadOnly)
50 stdout = Attribute("stdout", "Standard output", flags = Flags.ReadOnly)
51 stderr = Attribute("stderr", "Standard error", flags = Flags.ReadOnly)
53 tear_down = Attribute("tearDown", "Bash script to be executed before
54 releasing the resource", flags = Flags.ReadOnly)
56 cls._register_attribute(command)
57 cls._register_attribute(env)
58 cls._register_attribute(sudo)
59 cls._register_attribute(depends)
60 cls._register_attribute(sources)
61 cls._register_attribute(build)
62 cls._register_attribute(install)
63 cls._register_attribute(stdin)
64 cls._register_attribute(stdout)
65 cls._register_attribute(stderr)
66 cls._register_attribute(tear_down)
68 def __init__(self, ec, guid):
69 super(LinuxApplication, self).__init__(ec, guid)
72 self._home = "${HOME}/app-%s" % self.box.guid
75 self._logger = logging.getLogger("neco.linux.Application.%d" % guid)
97 def provision(self, filters = None):
105 dst = os.path.join(self.home, "app.sh")
107 # Create shell script with the command
108 # This way, complex commands and scripts can be ran seamlessly
111 env = self.get("env")
113 for envkey, envvals in env.iteritems():
114 for envval in envvals:
115 cmd += 'export %s=%s\n' % (envkey, envval)
117 cmd += self.get("command")
118 self.api.upload(cmd, dst)
120 command = 'bash ./app.sh'
121 stdin = 'stdin' if self.get("stdin") else None
122 self.api.run(command, self.home, stdin = stdin)
123 self._pid, self._ppid = self.api.checkpid(self.app_home)
126 self._state = ResourceState.STOPPED
129 tear_down = self.get("tearDown")
131 self.api.execute(tear_down)
133 return self.api.kill(self.pid, self.ppid)
136 return self.api.status(self.pid, self.ppid)
138 def make_app_home(self):
139 self.api.mkdir(self.home)
141 stdin = self.get("stdin")
143 self.api.upload(stdin, os.path.join(self.home, 'stdin'))
145 def _validate_connection(self, guid):
148 # XXX: What if it is connected to more than one node?
149 resources = self.find_resources(exact_tags = [tags.NODE])
150 self._node = resources[0] if len(resources) == 1 else None