SSHApi functionality migrated to LinuxNode
[nepi.git] / src / neco / resources / linux / application.py
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
4
5 import logging
6
7 @clsinit
8 class LinuxApplication(ResourceManager):
9     _rtype = "LinuxApplication"
10
11     @classmethod
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)
52
53         tear_down = Attribute("tearDown", "Bash script to be executed before
54                 releasing the resource", flags = Flags.ReadOnly)
55
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)
67
68     def __init__(self, ec, guid):
69         super(LinuxApplication, self).__init__(ec, guid)
70         self._pid = None
71         self._ppid = None
72         self._home = "${HOME}/app-%s" % self.box.guid
73         self._node = None
74
75         self._logger = logging.getLogger("neco.linux.Application.%d" % guid)
76
77     @property
78     def api(self):
79         return self.node.api
80
81     @property
82     def node(self):
83         self._node
84
85     @property
86     def home(self):
87         return self._home
88
89     @property
90     def pid(self):
91         return self._pid
92
93     @property
94     def ppid(self):
95         return self._ppid
96
97     def provision(self, filters = None):
98         # clean home
99         # upload
100         # build  
101         # Install stuff!!
102         pass
103
104     def start(self):
105         dst = os.path.join(self.home, "app.sh")
106         
107         # Create shell script with the command
108         # This way, complex commands and scripts can be ran seamlessly
109         # sync files
110         cmd = ""
111         env = self.get("env")
112         if env:
113             for envkey, envvals in env.iteritems():
114                 for envval in envvals:
115                     cmd += 'export %s=%s\n' % (envkey, envval)
116
117         cmd += self.get("command")
118         self.api.upload(cmd, dst)
119
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)
124
125     def stop(self):
126         # Kill
127         self._state = ResourceState.STOPPED
128
129     def release(self):
130         tear_down = self.get("tearDown")
131         if tear_down:
132             self.api.execute(tear_down)
133
134         return self.api.kill(self.pid, self.ppid)
135
136     def status(self):
137         return self.api.status(self.pid, self.ppid)
138
139     def make_app_home(self):
140         self.api.mkdir(self.home)
141
142         stdin = self.get("stdin")
143         if stdin:
144             self.api.upload(stdin, os.path.join(self.home, 'stdin'))
145
146     def _validate_connection(self, guid):
147         # TODO: Validate!
148         return True
149         # XXX: What if it is connected to more than one node?
150         resources = self.find_resources(exact_tags = [tags.NODE])
151         self._node = resources[0] if len(resources) == 1 else None
152         return self._node
153
154
155