1 from neco.execution.resource import Resource
2 from neco.util.sshfuncs import eintr_retry, 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:
43 msg = "Can't resolve package management system. Unknown OS."
44 self._logger.error(msg)
45 raise RuntimeError(msg)
49 def install(self, packages):
50 if not isinstance(packages, list):
54 self.execute("%s -y install %s" % (self.pm, p), sudo = True,
57 def uninstall(self, packages):
58 if not isinstance(packages, list):
62 self.execute("%s -y remove %s" % (self.pm, p), sudo = True,
65 def upload(self, src, dst):
66 if not os.path.isfile(src):
67 src = cStringIO.StringIO(src)
69 (out, err), proc = eintr_retry(rcopy)(
74 identity_file = self.identity_file)
77 msg = "Error uploading to %s got:\n%s%s" %\
78 (self.host or self.ip, out, err)
79 self._logger.error(msg)
80 raise RuntimeError(msg)
82 def is_alive(self, verbose = False):
83 (out, err), proc = eintr_retry(rexec)(
88 identity_file = self.identity_file,
90 err_on_timeout = False,
95 self._logger.warn("Unresponsive node %s got:\n%s%s", self.host, out, err)
97 elif out.strip().startswith('ALIVE'):
101 self._logger.warn("Unresponsive node %s got:\n%s%s", self.host, out, err)
104 def mkdir(self, path, clean = True):
109 "mkdir -p %s" % path,
114 def rmdir(self, path):
121 def execute(self, command,
128 err_on_timeout = True,
129 connect_timeout = 30,
131 """ Notice that this invocation will block until the
132 execution finishes. If this is not the desired behavior,
133 use 'run' instead."""
134 (out, err), proc = eintr_retry(rexec)(
136 self.host or self.ip,
142 identity_file = self.identity_file,
146 err_on_timeout = err_on_timeout,
147 connect_timeout = connect_timeout,
148 persistent = persistent)
151 msg = "Failed to execute command %s at node %s: %s %s" % \
152 (command, self.host or self.ip, out, err,)
153 self._logger.warn(msg)
154 raise RuntimeError(msg)
158 def run(self, command, home,
163 self._logger.info("Running %s", command)
165 # Start process in a "daemonized" way, using nohup and heavy
166 # stdin/out redirection to avoid connection issues
167 (out,err), proc = rspawn(
171 stdin = stdin if stdin is not None else '/dev/null',
172 stdout = stdout if stdout else '/dev/null',
173 stderr = stderr if stderr else '/dev/null',
178 identity_file = self.identity_file
182 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
184 def checkpid(self, path):
186 # NOTE: wait a bit for the pidfile to be created
187 pidtuple = rcheck_pid(
188 os.path.join(path, 'pid'),
192 identity_file = self.identity_file
197 def status(self, pid, ppid):
203 identity_file = self.identity_file
208 def kill(self, pid, ppid, sudo = False):
209 status = self.status(pid, ppid)
210 if status == RUNNING:
211 # kill by ppid+pid - SIGTERM first, then try SIGKILL
218 identity_file = self.identity_file