1 from neco.execution.resource import Resource
2 from neco.util.sshfuncs import eintr_retry, shell_escape, rexec, rcopy, \
3 rspawn, rcheck_pid, rstatus, rkill, RUNNING
9 class LinuxNode(Resource):
10 def __init__(self, box, ec):
11 super(LinuxNode, self).__init__(box, ec)
16 self.identity_file = None
17 # packet management system - either yum or apt for now...
22 self._logger = logging.getLogger("neco.resources.base.LinuxNode.%s" %\
24 self._logger.setLevel(getattr(logging, loglevel.upper()))
31 if (not (self.host or self.ip) or not self.user):
32 msg = "Can't resolve package management system. Insufficient data."
33 self._logger.error(msg)
34 raise RuntimeError(msg)
36 out = self.execute("cat /etc/issue")
38 if out.find("Fedora") == 0:
40 elif out.find("Debian") == 0 or out.find("Ubuntu") ==0:
41 self._pm = "apt-get -y "
43 msg = "Can't resolve package management system. Unknown OS."
44 self._logger.error(msg)
45 raise RuntimeError(msg)
49 def execute(self, command,
56 err_on_timeout = True,
59 """ Notice that this invocation will block until the
60 execution finishes. If this is not the desired behavior,
62 (out, err), proc = eintr_retry(rexec)(
70 identity_file = self.identity_file,
74 err_on_timeout = err_on_timeout,
75 connect_timeout = connect_timeout,
76 persistent = persistent)
79 msg = "Failed to execute command %s at node %s: %s %s" % \
80 (command, self.host or self.ip, out, err,)
81 self._logger.warn(msg)
82 raise RuntimeError(msg)
86 def package_install(self, dependencies):
87 if not isinstance(dependencies, list):
88 dependencies = [dependencies]
90 for d in dependencies:
91 self.execute("%s install %s" % (self.pm, d), sudo = True,
94 def upload(self, src, dst):
95 if not os.path.isfile(src):
96 src = cStringIO.StringIO(src)
98 (out, err), proc = eintr_retry(rcopy)(
100 self.host or self.ip,
103 identity_file = self.identity_file)
106 msg = "Error uploading to %s got:\n%s%s" %\
107 (self.host or self.ip, out, err)
108 self._logger.error(msg)
109 raise RuntimeError(msg)
111 def is_alive(self, verbose = False):
112 (out, err), proc = eintr_retry(rexec)(
114 self.host or self.ip,
117 identity_file = self.identity_file,
119 err_on_timeout = False,
123 self._logger.warn("Unresponsive node %s got:\n%s%s", self.host, out, err)
125 elif out.strip().startswith('ALIVE'):
128 self._logger.warn("Unresponsive node %s got:\n%s%s", self.host, out, err)
131 def mkdir(self, path, clean = True):
134 "rm -f %s" % shell_escape(path),
140 "mkdir -p %s" % shell_escape(path),
145 def run(self, command, home,
150 self._logger.info("Running %s", command)
152 # Start process in a "daemonized" way, using nohup and heavy
153 # stdin/out redirection to avoid connection issues
154 (out,err), proc = rspawn(
158 stdin = stdin if stdin is not None else '/dev/null',
159 stdout = stdout if stdout else '/dev/null',
160 stderr = stderr if stderr else '/dev/null',
165 identity_file = self.identity_file
169 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
171 def checkpid(self, path):
173 # NOTE: wait a bit for the pidfile to be created
174 pidtuple = rcheck_pid(
175 os.path.join(path, 'pid'),
179 identity_file = self.identity_file
184 def status(self, pid, ppid):
190 identity_file = self.identity_file
195 def kill(self, pid, ppid, sudo = False):
196 status = self.status(pid, ppid)
197 if status == RUNNING:
198 # kill by ppid+pid - SIGTERM first, then try SIGKILL
205 identity_file = self.identity_file