Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 7c2e922e

History | View | Annotate | Download (110.5 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 600535f0 Manuel Franceschini
# Copyright (C) 2006, 2007, 2010 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 58885d79 Iustin Pop
"""Ganeti utility module.
23 58885d79 Iustin Pop

24 58885d79 Iustin Pop
This module holds functions that can be used in both daemons (all) and
25 58885d79 Iustin Pop
the command line scripts.
26 899d2a81 Michael Hanselmann

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import os
31 c1dd99d4 Michael Hanselmann
import sys
32 a8083063 Iustin Pop
import time
33 113b55aa Iustin Pop
import subprocess
34 a8083063 Iustin Pop
import re
35 a8083063 Iustin Pop
import socket
36 a8083063 Iustin Pop
import tempfile
37 a8083063 Iustin Pop
import shutil
38 4ca1b175 Alexander Schreiber
import errno
39 2f8b60b3 Iustin Pop
import pwd
40 78feb6fb Guido Trotter
import itertools
41 9c233417 Iustin Pop
import select
42 9c233417 Iustin Pop
import fcntl
43 8f765069 Iustin Pop
import resource
44 bb698c1f Iustin Pop
import logging
45 551b6283 Iustin Pop
import logging.handlers
46 de499029 Michael Hanselmann
import signal
47 bdd5e420 Michael Hanselmann
import OpenSSL
48 27e46076 Michael Hanselmann
import datetime
49 27e46076 Michael Hanselmann
import calendar
50 68857643 Michael Hanselmann
import hmac
51 339be5a8 Michael Hanselmann
import collections
52 9c233417 Iustin Pop
53 9c233417 Iustin Pop
from cStringIO import StringIO
54 a8083063 Iustin Pop
55 7ffe8fba Carlos Valiente
try:
56 23e0ef8c Guido Trotter
  # pylint: disable-msg=F0401
57 4b6fa0bf Luca Bigliardi
  import ctypes
58 4b6fa0bf Luca Bigliardi
except ImportError:
59 4b6fa0bf Luca Bigliardi
  ctypes = None
60 4b6fa0bf Luca Bigliardi
61 a8083063 Iustin Pop
from ganeti import errors
62 3aecd2c7 Iustin Pop
from ganeti import constants
63 716a32cb Guido Trotter
from ganeti import compat
64 a8083063 Iustin Pop
65 16abfbc2 Alexander Schreiber
66 a8083063 Iustin Pop
_locksheld = []
67 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
68 a8083063 Iustin Pop
69 e67bd559 Michael Hanselmann
debug_locks = False
70 58885d79 Iustin Pop
71 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
72 b74159ee Iustin Pop
no_fork = False
73 f362096f Iustin Pop
74 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
75 13998ef2 Michael Hanselmann
76 68857643 Michael Hanselmann
HEX_CHAR_RE = r"[a-zA-Z0-9]"
77 68857643 Michael Hanselmann
VALID_X509_SIGNATURE_SALT = re.compile("^%s+$" % HEX_CHAR_RE, re.S)
78 68857643 Michael Hanselmann
X509_SIGNATURE = re.compile(r"^%s:\s*(?P<salt>%s+)/(?P<sign>%s+)$" %
79 68857643 Michael Hanselmann
                            (re.escape(constants.X509_CERT_SIGNATURE_HEADER),
80 68857643 Michael Hanselmann
                             HEX_CHAR_RE, HEX_CHAR_RE),
81 68857643 Michael Hanselmann
                            re.S | re.I)
82 68857643 Michael Hanselmann
83 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
84 28f34048 Michael Hanselmann
85 05636402 Guido Trotter
UUID_RE = re.compile('^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-'
86 05636402 Guido Trotter
                     '[a-f0-9]{4}-[a-f0-9]{12}$')
87 05636402 Guido Trotter
88 24d70417 Michael Hanselmann
# Certificate verification results
89 24d70417 Michael Hanselmann
(CERT_WARNING,
90 24d70417 Michael Hanselmann
 CERT_ERROR) = range(1, 3)
91 24d70417 Michael Hanselmann
92 4b6fa0bf Luca Bigliardi
# Flags for mlockall() (from bits/mman.h)
93 4b6fa0bf Luca Bigliardi
_MCL_CURRENT = 1
94 4b6fa0bf Luca Bigliardi
_MCL_FUTURE = 2
95 4b6fa0bf Luca Bigliardi
96 8fb00704 Iustin Pop
#: MAC checker regexp
97 8fb00704 Iustin Pop
_MAC_CHECK = re.compile("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", re.I)
98 8fb00704 Iustin Pop
99 c74cda62 René Nussbaumer
(_TIMEOUT_NONE,
100 c74cda62 René Nussbaumer
 _TIMEOUT_TERM,
101 c74cda62 René Nussbaumer
 _TIMEOUT_KILL) = range(3)
102 c74cda62 René Nussbaumer
103 0b5303da Iustin Pop
#: Shell param checker regexp
104 0b5303da Iustin Pop
_SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
105 0b5303da Iustin Pop
106 0b5303da Iustin Pop
#: Unit checker regexp
107 0b5303da Iustin Pop
_PARSEUNIT_REGEX = re.compile(r"^([.\d]+)\s*([a-zA-Z]+)?$")
108 0b5303da Iustin Pop
109 0b5303da Iustin Pop
#: ASN1 time regexp
110 f394c0de Iustin Pop
_ASN1_TIME_REGEX = re.compile(r"^(\d+)([-+]\d\d)(\d\d)$")
111 0b5303da Iustin Pop
112 7c0d6283 Michael Hanselmann
113 a8083063 Iustin Pop
class RunResult(object):
114 58885d79 Iustin Pop
  """Holds the result of running external programs.
115 58885d79 Iustin Pop

116 58885d79 Iustin Pop
  @type exit_code: int
117 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
118 58885d79 Iustin Pop
      didn't exit())
119 58885d79 Iustin Pop
  @type signal: int or None
120 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
121 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
122 58885d79 Iustin Pop
  @type stdout: str
123 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
124 58885d79 Iustin Pop
  @type stderr: str
125 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
126 58885d79 Iustin Pop
  @type failed: boolean
127 58885d79 Iustin Pop
  @ivar failed: True in case the program was
128 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
129 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
130 a8083063 Iustin Pop

131 a8083063 Iustin Pop
  """
132 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
133 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
136 c74cda62 René Nussbaumer
  def __init__(self, exit_code, signal_, stdout, stderr, cmd, timeout_action,
137 c74cda62 René Nussbaumer
               timeout):
138 a8083063 Iustin Pop
    self.cmd = cmd
139 a8083063 Iustin Pop
    self.exit_code = exit_code
140 38206f3c Iustin Pop
    self.signal = signal_
141 a8083063 Iustin Pop
    self.stdout = stdout
142 a8083063 Iustin Pop
    self.stderr = stderr
143 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
144 a8083063 Iustin Pop
145 c74cda62 René Nussbaumer
    fail_msgs = []
146 a8083063 Iustin Pop
    if self.signal is not None:
147 c74cda62 René Nussbaumer
      fail_msgs.append("terminated by signal %s" % self.signal)
148 a8083063 Iustin Pop
    elif self.exit_code is not None:
149 c74cda62 René Nussbaumer
      fail_msgs.append("exited with exit code %s" % self.exit_code)
150 a8083063 Iustin Pop
    else:
151 c74cda62 René Nussbaumer
      fail_msgs.append("unable to determine termination reason")
152 c74cda62 René Nussbaumer
153 c74cda62 René Nussbaumer
    if timeout_action == _TIMEOUT_TERM:
154 c74cda62 René Nussbaumer
      fail_msgs.append("terminated after timeout of %.2f seconds" % timeout)
155 c74cda62 René Nussbaumer
    elif timeout_action == _TIMEOUT_KILL:
156 c74cda62 René Nussbaumer
      fail_msgs.append(("force termination after timeout of %.2f seconds"
157 c74cda62 René Nussbaumer
                        " and linger for another %.2f seconds") %
158 c74cda62 René Nussbaumer
                       (timeout, constants.CHILD_LINGER_TIMEOUT))
159 c74cda62 René Nussbaumer
160 c74cda62 René Nussbaumer
    if fail_msgs and self.failed:
161 c74cda62 René Nussbaumer
      self.fail_reason = CommaJoin(fail_msgs)
162 a8083063 Iustin Pop
163 bb698c1f Iustin Pop
    if self.failed:
164 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
165 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
166 f362096f Iustin Pop
167 a8083063 Iustin Pop
  def _GetOutput(self):
168 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
    """
171 a8083063 Iustin Pop
    return self.stdout + self.stderr
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
174 a8083063 Iustin Pop
175 a8083063 Iustin Pop
176 bb3776b4 Michael Hanselmann
def _BuildCmdEnvironment(env, reset):
177 c1dd99d4 Michael Hanselmann
  """Builds the environment for an external program.
178 c1dd99d4 Michael Hanselmann

179 c1dd99d4 Michael Hanselmann
  """
180 bb3776b4 Michael Hanselmann
  if reset:
181 bb3776b4 Michael Hanselmann
    cmd_env = {}
182 bb3776b4 Michael Hanselmann
  else:
183 bb3776b4 Michael Hanselmann
    cmd_env = os.environ.copy()
184 bb3776b4 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
185 bb3776b4 Michael Hanselmann
186 c1dd99d4 Michael Hanselmann
  if env is not None:
187 c1dd99d4 Michael Hanselmann
    cmd_env.update(env)
188 bb3776b4 Michael Hanselmann
189 c1dd99d4 Michael Hanselmann
  return cmd_env
190 c1dd99d4 Michael Hanselmann
191 c1dd99d4 Michael Hanselmann
192 0963d545 René Nussbaumer
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False,
193 c74cda62 René Nussbaumer
           interactive=False, timeout=None):
194 a8083063 Iustin Pop
  """Execute a (shell) command.
195 a8083063 Iustin Pop

196 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
197 a8083063 Iustin Pop
  closed.
198 a8083063 Iustin Pop

199 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
200 36117c2b Iustin Pop
  @param cmd: Command to run
201 2557ff82 Guido Trotter
  @type env: dict
202 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
203 36117c2b Iustin Pop
  @type output: str
204 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
205 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
206 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
207 8797df43 Iustin Pop
  @type cwd: string
208 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
209 8797df43 Iustin Pop
      directory for the command; the default will be /
210 bf4daac9 Guido Trotter
  @type reset_env: boolean
211 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
212 0963d545 René Nussbaumer
  @type interactive: boolean
213 0963d545 René Nussbaumer
  @param interactive: weather we pipe stdin, stdout and stderr
214 0963d545 René Nussbaumer
                      (default behaviour) or run the command interactive
215 c74cda62 René Nussbaumer
  @type timeout: int
216 c74cda62 René Nussbaumer
  @param timeout: If not None, timeout in seconds until child process gets
217 c74cda62 René Nussbaumer
                  killed
218 36117c2b Iustin Pop
  @rtype: L{RunResult}
219 58885d79 Iustin Pop
  @return: RunResult instance
220 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
221 a8083063 Iustin Pop

222 a8083063 Iustin Pop
  """
223 b74159ee Iustin Pop
  if no_fork:
224 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
225 b74159ee Iustin Pop
226 0963d545 René Nussbaumer
  if output and interactive:
227 0963d545 René Nussbaumer
    raise errors.ProgrammerError("Parameters 'output' and 'interactive' can"
228 0963d545 René Nussbaumer
                                 " not be provided at the same time")
229 0963d545 René Nussbaumer
230 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
231 c1dd99d4 Michael Hanselmann
    strcmd = cmd
232 c1dd99d4 Michael Hanselmann
    shell = True
233 c1dd99d4 Michael Hanselmann
  else:
234 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
235 c1dd99d4 Michael Hanselmann
    strcmd = ShellQuoteArgs(cmd)
236 113b55aa Iustin Pop
    shell = False
237 c1dd99d4 Michael Hanselmann
238 c1dd99d4 Michael Hanselmann
  if output:
239 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s, output file '%s'", strcmd, output)
240 113b55aa Iustin Pop
  else:
241 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s", strcmd)
242 2557ff82 Guido Trotter
243 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, reset_env)
244 2557ff82 Guido Trotter
245 c803b052 Iustin Pop
  try:
246 c803b052 Iustin Pop
    if output is None:
247 c74cda62 René Nussbaumer
      out, err, status, timeout_action = _RunCmdPipe(cmd, cmd_env, shell, cwd,
248 c74cda62 René Nussbaumer
                                                     interactive, timeout)
249 c803b052 Iustin Pop
    else:
250 c74cda62 René Nussbaumer
      timeout_action = _TIMEOUT_NONE
251 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
252 c803b052 Iustin Pop
      out = err = ""
253 c803b052 Iustin Pop
  except OSError, err:
254 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
255 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
256 c803b052 Iustin Pop
                               (strcmd, err))
257 c803b052 Iustin Pop
    else:
258 c803b052 Iustin Pop
      raise
259 36117c2b Iustin Pop
260 36117c2b Iustin Pop
  if status >= 0:
261 36117c2b Iustin Pop
    exitcode = status
262 36117c2b Iustin Pop
    signal_ = None
263 36117c2b Iustin Pop
  else:
264 36117c2b Iustin Pop
    exitcode = None
265 36117c2b Iustin Pop
    signal_ = -status
266 36117c2b Iustin Pop
267 c74cda62 René Nussbaumer
  return RunResult(exitcode, signal_, out, err, strcmd, timeout_action, timeout)
268 36117c2b Iustin Pop
269 ae59efea Michael Hanselmann
270 0260032c Iustin Pop
def SetupDaemonEnv(cwd="/", umask=077):
271 0260032c Iustin Pop
  """Setup a daemon's environment.
272 0260032c Iustin Pop

273 0260032c Iustin Pop
  This should be called between the first and second fork, due to
274 0260032c Iustin Pop
  setsid usage.
275 0260032c Iustin Pop

276 0260032c Iustin Pop
  @param cwd: the directory to which to chdir
277 0260032c Iustin Pop
  @param umask: the umask to setup
278 0260032c Iustin Pop

279 0260032c Iustin Pop
  """
280 0260032c Iustin Pop
  os.chdir(cwd)
281 0260032c Iustin Pop
  os.umask(umask)
282 0260032c Iustin Pop
  os.setsid()
283 0260032c Iustin Pop
284 0260032c Iustin Pop
285 79634555 Iustin Pop
def SetupDaemonFDs(output_file, output_fd):
286 79634555 Iustin Pop
  """Setups up a daemon's file descriptors.
287 79634555 Iustin Pop

288 79634555 Iustin Pop
  @param output_file: if not None, the file to which to redirect
289 79634555 Iustin Pop
      stdout/stderr
290 79634555 Iustin Pop
  @param output_fd: if not None, the file descriptor for stdout/stderr
291 79634555 Iustin Pop

292 79634555 Iustin Pop
  """
293 79634555 Iustin Pop
  # check that at most one is defined
294 79634555 Iustin Pop
  assert [output_file, output_fd].count(None) >= 1
295 79634555 Iustin Pop
296 79634555 Iustin Pop
  # Open /dev/null (read-only, only for stdin)
297 79634555 Iustin Pop
  devnull_fd = os.open(os.devnull, os.O_RDONLY)
298 79634555 Iustin Pop
299 79634555 Iustin Pop
  if output_fd is not None:
300 79634555 Iustin Pop
    pass
301 79634555 Iustin Pop
  elif output_file is not None:
302 79634555 Iustin Pop
    # Open output file
303 79634555 Iustin Pop
    try:
304 79634555 Iustin Pop
      output_fd = os.open(output_file,
305 79634555 Iustin Pop
                          os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
306 79634555 Iustin Pop
    except EnvironmentError, err:
307 79634555 Iustin Pop
      raise Exception("Opening output file failed: %s" % err)
308 79634555 Iustin Pop
  else:
309 79634555 Iustin Pop
    output_fd = os.open(os.devnull, os.O_WRONLY)
310 79634555 Iustin Pop
311 79634555 Iustin Pop
  # Redirect standard I/O
312 79634555 Iustin Pop
  os.dup2(devnull_fd, 0)
313 79634555 Iustin Pop
  os.dup2(output_fd, 1)
314 79634555 Iustin Pop
  os.dup2(output_fd, 2)
315 79634555 Iustin Pop
316 79634555 Iustin Pop
317 c1dd99d4 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
318 c1dd99d4 Michael Hanselmann
                pidfile=None):
319 c1dd99d4 Michael Hanselmann
  """Start a daemon process after forking twice.
320 c1dd99d4 Michael Hanselmann

321 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
322 c1dd99d4 Michael Hanselmann
  @param cmd: Command to run
323 c1dd99d4 Michael Hanselmann
  @type env: dict
324 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
325 c1dd99d4 Michael Hanselmann
  @type cwd: string
326 c1dd99d4 Michael Hanselmann
  @param cwd: Working directory for the program
327 c1dd99d4 Michael Hanselmann
  @type output: string
328 c1dd99d4 Michael Hanselmann
  @param output: Path to file in which to save the output
329 c1dd99d4 Michael Hanselmann
  @type output_fd: int
330 c1dd99d4 Michael Hanselmann
  @param output_fd: File descriptor for output
331 c1dd99d4 Michael Hanselmann
  @type pidfile: string
332 c1dd99d4 Michael Hanselmann
  @param pidfile: Process ID file
333 c1dd99d4 Michael Hanselmann
  @rtype: int
334 c1dd99d4 Michael Hanselmann
  @return: Daemon process ID
335 c1dd99d4 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
336 c1dd99d4 Michael Hanselmann

337 c1dd99d4 Michael Hanselmann
  """
338 c1dd99d4 Michael Hanselmann
  if no_fork:
339 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("utils.StartDaemon() called with fork()"
340 c1dd99d4 Michael Hanselmann
                                 " disabled")
341 c1dd99d4 Michael Hanselmann
342 c1dd99d4 Michael Hanselmann
  if output and not (bool(output) ^ (output_fd is not None)):
343 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be"
344 c1dd99d4 Michael Hanselmann
                                 " specified")
345 c1dd99d4 Michael Hanselmann
346 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
347 c1dd99d4 Michael Hanselmann
    cmd = ["/bin/sh", "-c", cmd]
348 c1dd99d4 Michael Hanselmann
349 c1dd99d4 Michael Hanselmann
  strcmd = ShellQuoteArgs(cmd)
350 c1dd99d4 Michael Hanselmann
351 c1dd99d4 Michael Hanselmann
  if output:
352 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
353 c1dd99d4 Michael Hanselmann
  else:
354 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s", strcmd)
355 c1dd99d4 Michael Hanselmann
356 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, False)
357 c1dd99d4 Michael Hanselmann
358 c1dd99d4 Michael Hanselmann
  # Create pipe for sending PID back
359 c1dd99d4 Michael Hanselmann
  (pidpipe_read, pidpipe_write) = os.pipe()
360 c1dd99d4 Michael Hanselmann
  try:
361 c1dd99d4 Michael Hanselmann
    try:
362 c1dd99d4 Michael Hanselmann
      # Create pipe for sending error messages
363 c1dd99d4 Michael Hanselmann
      (errpipe_read, errpipe_write) = os.pipe()
364 c1dd99d4 Michael Hanselmann
      try:
365 c1dd99d4 Michael Hanselmann
        try:
366 c1dd99d4 Michael Hanselmann
          # First fork
367 c1dd99d4 Michael Hanselmann
          pid = os.fork()
368 c1dd99d4 Michael Hanselmann
          if pid == 0:
369 c1dd99d4 Michael Hanselmann
            try:
370 c1dd99d4 Michael Hanselmann
              # Child process, won't return
371 e0bb431e Michael Hanselmann
              _StartDaemonChild(errpipe_read, errpipe_write,
372 e0bb431e Michael Hanselmann
                                pidpipe_read, pidpipe_write,
373 e0bb431e Michael Hanselmann
                                cmd, cmd_env, cwd,
374 e0bb431e Michael Hanselmann
                                output, output_fd, pidfile)
375 c1dd99d4 Michael Hanselmann
            finally:
376 c1dd99d4 Michael Hanselmann
              # Well, maybe child process failed
377 e0bb431e Michael Hanselmann
              os._exit(1) # pylint: disable-msg=W0212
378 c1dd99d4 Michael Hanselmann
        finally:
379 c1dd99d4 Michael Hanselmann
          _CloseFDNoErr(errpipe_write)
380 c1dd99d4 Michael Hanselmann
381 b78aa8c2 Iustin Pop
        # Wait for daemon to be started (or an error message to
382 b78aa8c2 Iustin Pop
        # arrive) and read up to 100 KB as an error message
383 c1dd99d4 Michael Hanselmann
        errormsg = RetryOnSignal(os.read, errpipe_read, 100 * 1024)
384 c1dd99d4 Michael Hanselmann
      finally:
385 c1dd99d4 Michael Hanselmann
        _CloseFDNoErr(errpipe_read)
386 c1dd99d4 Michael Hanselmann
    finally:
387 c1dd99d4 Michael Hanselmann
      _CloseFDNoErr(pidpipe_write)
388 c1dd99d4 Michael Hanselmann
389 c1dd99d4 Michael Hanselmann
    # Read up to 128 bytes for PID
390 c1dd99d4 Michael Hanselmann
    pidtext = RetryOnSignal(os.read, pidpipe_read, 128)
391 c1dd99d4 Michael Hanselmann
  finally:
392 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
393 c1dd99d4 Michael Hanselmann
394 c1dd99d4 Michael Hanselmann
  # Try to avoid zombies by waiting for child process
395 c1dd99d4 Michael Hanselmann
  try:
396 c1dd99d4 Michael Hanselmann
    os.waitpid(pid, 0)
397 c1dd99d4 Michael Hanselmann
  except OSError:
398 c1dd99d4 Michael Hanselmann
    pass
399 c1dd99d4 Michael Hanselmann
400 c1dd99d4 Michael Hanselmann
  if errormsg:
401 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error when starting daemon process: %r" %
402 c1dd99d4 Michael Hanselmann
                             errormsg)
403 c1dd99d4 Michael Hanselmann
404 c1dd99d4 Michael Hanselmann
  try:
405 c1dd99d4 Michael Hanselmann
    return int(pidtext)
406 c1dd99d4 Michael Hanselmann
  except (ValueError, TypeError), err:
407 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error while trying to parse PID %r: %s" %
408 c1dd99d4 Michael Hanselmann
                             (pidtext, err))
409 c1dd99d4 Michael Hanselmann
410 c1dd99d4 Michael Hanselmann
411 e0bb431e Michael Hanselmann
def _StartDaemonChild(errpipe_read, errpipe_write,
412 e0bb431e Michael Hanselmann
                      pidpipe_read, pidpipe_write,
413 e0bb431e Michael Hanselmann
                      args, env, cwd,
414 e0bb431e Michael Hanselmann
                      output, fd_output, pidfile):
415 c1dd99d4 Michael Hanselmann
  """Child process for starting daemon.
416 c1dd99d4 Michael Hanselmann

417 c1dd99d4 Michael Hanselmann
  """
418 c1dd99d4 Michael Hanselmann
  try:
419 c1dd99d4 Michael Hanselmann
    # Close parent's side
420 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(errpipe_read)
421 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
422 c1dd99d4 Michael Hanselmann
423 c1dd99d4 Michael Hanselmann
    # First child process
424 0260032c Iustin Pop
    SetupDaemonEnv()
425 c1dd99d4 Michael Hanselmann
426 c1dd99d4 Michael Hanselmann
    # And fork for the second time
427 c1dd99d4 Michael Hanselmann
    pid = os.fork()
428 c1dd99d4 Michael Hanselmann
    if pid != 0:
429 c1dd99d4 Michael Hanselmann
      # Exit first child process
430 c1dd99d4 Michael Hanselmann
      os._exit(0) # pylint: disable-msg=W0212
431 c1dd99d4 Michael Hanselmann
432 0260032c Iustin Pop
    # Make sure pipe is closed on execv* (and thereby notifies
433 0260032c Iustin Pop
    # original process)
434 c1dd99d4 Michael Hanselmann
    SetCloseOnExecFlag(errpipe_write, True)
435 c1dd99d4 Michael Hanselmann
436 c1dd99d4 Michael Hanselmann
    # List of file descriptors to be left open
437 c1dd99d4 Michael Hanselmann
    noclose_fds = [errpipe_write]
438 c1dd99d4 Michael Hanselmann
439 c1dd99d4 Michael Hanselmann
    # Open PID file
440 c1dd99d4 Michael Hanselmann
    if pidfile:
441 5c4d37f9 Iustin Pop
      fd_pidfile = WritePidFile(pidfile)
442 c1dd99d4 Michael Hanselmann
443 c1dd99d4 Michael Hanselmann
      # Keeping the file open to hold the lock
444 c1dd99d4 Michael Hanselmann
      noclose_fds.append(fd_pidfile)
445 c1dd99d4 Michael Hanselmann
446 c1dd99d4 Michael Hanselmann
      SetCloseOnExecFlag(fd_pidfile, False)
447 c1dd99d4 Michael Hanselmann
    else:
448 c1dd99d4 Michael Hanselmann
      fd_pidfile = None
449 c1dd99d4 Michael Hanselmann
450 79634555 Iustin Pop
    SetupDaemonFDs(output, fd_output)
451 c1dd99d4 Michael Hanselmann
452 c1dd99d4 Michael Hanselmann
    # Send daemon PID to parent
453 c1dd99d4 Michael Hanselmann
    RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
454 c1dd99d4 Michael Hanselmann
455 c1dd99d4 Michael Hanselmann
    # Close all file descriptors except stdio and error message pipe
456 c1dd99d4 Michael Hanselmann
    CloseFDs(noclose_fds=noclose_fds)
457 c1dd99d4 Michael Hanselmann
458 c1dd99d4 Michael Hanselmann
    # Change working directory
459 c1dd99d4 Michael Hanselmann
    os.chdir(cwd)
460 c1dd99d4 Michael Hanselmann
461 c1dd99d4 Michael Hanselmann
    if env is None:
462 c1dd99d4 Michael Hanselmann
      os.execvp(args[0], args)
463 c1dd99d4 Michael Hanselmann
    else:
464 c1dd99d4 Michael Hanselmann
      os.execvpe(args[0], args, env)
465 c1dd99d4 Michael Hanselmann
  except: # pylint: disable-msg=W0702
466 c1dd99d4 Michael Hanselmann
    try:
467 c1dd99d4 Michael Hanselmann
      # Report errors to original process
468 ed3920e3 Iustin Pop
      WriteErrorToFD(errpipe_write, str(sys.exc_info()[1]))
469 c1dd99d4 Michael Hanselmann
    except: # pylint: disable-msg=W0702
470 c1dd99d4 Michael Hanselmann
      # Ignore errors in error handling
471 c1dd99d4 Michael Hanselmann
      pass
472 c1dd99d4 Michael Hanselmann
473 c1dd99d4 Michael Hanselmann
  os._exit(1) # pylint: disable-msg=W0212
474 c1dd99d4 Michael Hanselmann
475 c1dd99d4 Michael Hanselmann
476 ed3920e3 Iustin Pop
def WriteErrorToFD(fd, err):
477 ed3920e3 Iustin Pop
  """Possibly write an error message to a fd.
478 ed3920e3 Iustin Pop

479 ed3920e3 Iustin Pop
  @type fd: None or int (file descriptor)
480 ed3920e3 Iustin Pop
  @param fd: if not None, the error will be written to this fd
481 ed3920e3 Iustin Pop
  @param err: string, the error message
482 ed3920e3 Iustin Pop

483 ed3920e3 Iustin Pop
  """
484 ed3920e3 Iustin Pop
  if fd is None:
485 ed3920e3 Iustin Pop
    return
486 ed3920e3 Iustin Pop
487 ed3920e3 Iustin Pop
  if not err:
488 ed3920e3 Iustin Pop
    err = "<unknown error>"
489 ed3920e3 Iustin Pop
490 ed3920e3 Iustin Pop
  RetryOnSignal(os.write, fd, err)
491 ed3920e3 Iustin Pop
492 ed3920e3 Iustin Pop
493 c74cda62 René Nussbaumer
def _CheckIfAlive(child):
494 c74cda62 René Nussbaumer
  """Raises L{RetryAgain} if child is still alive.
495 c74cda62 René Nussbaumer

496 c74cda62 René Nussbaumer
  @raises RetryAgain: If child is still alive
497 c74cda62 René Nussbaumer

498 c74cda62 René Nussbaumer
  """
499 c74cda62 René Nussbaumer
  if child.poll() is None:
500 c74cda62 René Nussbaumer
    raise RetryAgain()
501 c74cda62 René Nussbaumer
502 c74cda62 René Nussbaumer
503 c74cda62 René Nussbaumer
def _WaitForProcess(child, timeout):
504 c74cda62 René Nussbaumer
  """Waits for the child to terminate or until we reach timeout.
505 c74cda62 René Nussbaumer

506 c74cda62 René Nussbaumer
  """
507 c74cda62 René Nussbaumer
  try:
508 c74cda62 René Nussbaumer
    Retry(_CheckIfAlive, (1.0, 1.2, 5.0), max(0, timeout), args=[child])
509 c74cda62 René Nussbaumer
  except RetryTimeout:
510 c74cda62 René Nussbaumer
    pass
511 c74cda62 René Nussbaumer
512 c74cda62 René Nussbaumer
513 c74cda62 René Nussbaumer
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout,
514 c74cda62 René Nussbaumer
                _linger_timeout=constants.CHILD_LINGER_TIMEOUT):
515 36117c2b Iustin Pop
  """Run a command and return its output.
516 36117c2b Iustin Pop

517 36117c2b Iustin Pop
  @type  cmd: string or list
518 36117c2b Iustin Pop
  @param cmd: Command to run
519 36117c2b Iustin Pop
  @type env: dict
520 36117c2b Iustin Pop
  @param env: The environment to use
521 36117c2b Iustin Pop
  @type via_shell: bool
522 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
523 8797df43 Iustin Pop
  @type cwd: string
524 8797df43 Iustin Pop
  @param cwd: the working directory for the program
525 0963d545 René Nussbaumer
  @type interactive: boolean
526 0963d545 René Nussbaumer
  @param interactive: Run command interactive (without piping)
527 c74cda62 René Nussbaumer
  @type timeout: int
528 c74cda62 René Nussbaumer
  @param timeout: Timeout after the programm gets terminated
529 36117c2b Iustin Pop
  @rtype: tuple
530 36117c2b Iustin Pop
  @return: (out, err, status)
531 36117c2b Iustin Pop

532 36117c2b Iustin Pop
  """
533 9c233417 Iustin Pop
  poller = select.poll()
534 0963d545 René Nussbaumer
535 0963d545 René Nussbaumer
  stderr = subprocess.PIPE
536 0963d545 René Nussbaumer
  stdout = subprocess.PIPE
537 0963d545 René Nussbaumer
  stdin = subprocess.PIPE
538 0963d545 René Nussbaumer
539 0963d545 René Nussbaumer
  if interactive:
540 0963d545 René Nussbaumer
    stderr = stdout = stdin = None
541 0963d545 René Nussbaumer
542 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
543 0963d545 René Nussbaumer
                           stderr=stderr,
544 0963d545 René Nussbaumer
                           stdout=stdout,
545 0963d545 René Nussbaumer
                           stdin=stdin,
546 8797df43 Iustin Pop
                           close_fds=True, env=env,
547 8797df43 Iustin Pop
                           cwd=cwd)
548 113b55aa Iustin Pop
549 9c233417 Iustin Pop
  out = StringIO()
550 9c233417 Iustin Pop
  err = StringIO()
551 c74cda62 René Nussbaumer
552 c74cda62 René Nussbaumer
  linger_timeout = None
553 c74cda62 René Nussbaumer
554 c74cda62 René Nussbaumer
  if timeout is None:
555 c74cda62 René Nussbaumer
    poll_timeout = None
556 c74cda62 René Nussbaumer
  else:
557 c74cda62 René Nussbaumer
    poll_timeout = RunningTimeout(timeout, True).Remaining
558 c74cda62 René Nussbaumer
559 c74cda62 René Nussbaumer
  msg_timeout = ("Command %s (%d) run into execution timeout, terminating" %
560 c74cda62 René Nussbaumer
                 (cmd, child.pid))
561 c74cda62 René Nussbaumer
  msg_linger = ("Command %s (%d) run into linger timeout, killing" %
562 c74cda62 René Nussbaumer
                (cmd, child.pid))
563 c74cda62 René Nussbaumer
564 c74cda62 René Nussbaumer
  timeout_action = _TIMEOUT_NONE
565 c74cda62 René Nussbaumer
566 0963d545 René Nussbaumer
  if not interactive:
567 0963d545 René Nussbaumer
    child.stdin.close()
568 0963d545 René Nussbaumer
    poller.register(child.stdout, select.POLLIN)
569 0963d545 René Nussbaumer
    poller.register(child.stderr, select.POLLIN)
570 0963d545 René Nussbaumer
    fdmap = {
571 0963d545 René Nussbaumer
      child.stdout.fileno(): (out, child.stdout),
572 0963d545 René Nussbaumer
      child.stderr.fileno(): (err, child.stderr),
573 0963d545 René Nussbaumer
      }
574 0963d545 René Nussbaumer
    for fd in fdmap:
575 0963d545 René Nussbaumer
      SetNonblockFlag(fd, True)
576 0963d545 René Nussbaumer
577 0963d545 René Nussbaumer
    while fdmap:
578 c74cda62 René Nussbaumer
      if poll_timeout:
579 d05cf6fa Iustin Pop
        pt = poll_timeout() * 1000
580 d05cf6fa Iustin Pop
        if pt < 0:
581 c74cda62 René Nussbaumer
          if linger_timeout is None:
582 c74cda62 René Nussbaumer
            logging.warning(msg_timeout)
583 c74cda62 René Nussbaumer
            if child.poll() is None:
584 c74cda62 René Nussbaumer
              timeout_action = _TIMEOUT_TERM
585 c74cda62 René Nussbaumer
              IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM)
586 c74cda62 René Nussbaumer
            linger_timeout = RunningTimeout(_linger_timeout, True).Remaining
587 d05cf6fa Iustin Pop
          pt = linger_timeout() * 1000
588 d05cf6fa Iustin Pop
          if pt < 0:
589 c74cda62 René Nussbaumer
            break
590 c74cda62 René Nussbaumer
      else:
591 c74cda62 René Nussbaumer
        pt = None
592 c74cda62 René Nussbaumer
593 c74cda62 René Nussbaumer
      pollresult = RetryOnSignal(poller.poll, pt)
594 0963d545 René Nussbaumer
595 0963d545 René Nussbaumer
      for fd, event in pollresult:
596 0963d545 René Nussbaumer
        if event & select.POLLIN or event & select.POLLPRI:
597 0963d545 René Nussbaumer
          data = fdmap[fd][1].read()
598 0963d545 René Nussbaumer
          # no data from read signifies EOF (the same as POLLHUP)
599 0963d545 René Nussbaumer
          if not data:
600 0963d545 René Nussbaumer
            poller.unregister(fd)
601 0963d545 René Nussbaumer
            del fdmap[fd]
602 0963d545 René Nussbaumer
            continue
603 0963d545 René Nussbaumer
          fdmap[fd][0].write(data)
604 0963d545 René Nussbaumer
        if (event & select.POLLNVAL or event & select.POLLHUP or
605 0963d545 René Nussbaumer
            event & select.POLLERR):
606 9c233417 Iustin Pop
          poller.unregister(fd)
607 9c233417 Iustin Pop
          del fdmap[fd]
608 9c233417 Iustin Pop
609 c74cda62 René Nussbaumer
  if timeout is not None:
610 c74cda62 René Nussbaumer
    assert callable(poll_timeout)
611 c74cda62 René Nussbaumer
612 c74cda62 René Nussbaumer
    # We have no I/O left but it might still run
613 c74cda62 René Nussbaumer
    if child.poll() is None:
614 c74cda62 René Nussbaumer
      _WaitForProcess(child, poll_timeout())
615 c74cda62 René Nussbaumer
616 c74cda62 René Nussbaumer
    # Terminate if still alive after timeout
617 c74cda62 René Nussbaumer
    if child.poll() is None:
618 c74cda62 René Nussbaumer
      if linger_timeout is None:
619 c74cda62 René Nussbaumer
        logging.warning(msg_timeout)
620 c74cda62 René Nussbaumer
        timeout_action = _TIMEOUT_TERM
621 c74cda62 René Nussbaumer
        IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM)
622 c74cda62 René Nussbaumer
        lt = _linger_timeout
623 c74cda62 René Nussbaumer
      else:
624 c74cda62 René Nussbaumer
        lt = linger_timeout()
625 c74cda62 René Nussbaumer
      _WaitForProcess(child, lt)
626 c74cda62 René Nussbaumer
627 c74cda62 René Nussbaumer
    # Okay, still alive after timeout and linger timeout? Kill it!
628 c74cda62 René Nussbaumer
    if child.poll() is None:
629 c74cda62 René Nussbaumer
      timeout_action = _TIMEOUT_KILL
630 c74cda62 René Nussbaumer
      logging.warning(msg_linger)
631 c74cda62 René Nussbaumer
      IgnoreProcessNotFound(os.kill, child.pid, signal.SIGKILL)
632 c74cda62 René Nussbaumer
633 9c233417 Iustin Pop
  out = out.getvalue()
634 9c233417 Iustin Pop
  err = err.getvalue()
635 a8083063 Iustin Pop
636 a8083063 Iustin Pop
  status = child.wait()
637 c74cda62 René Nussbaumer
  return out, err, status, timeout_action
638 a8083063 Iustin Pop
639 36117c2b Iustin Pop
640 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
641 36117c2b Iustin Pop
  """Run a command and save its output to a file.
642 36117c2b Iustin Pop

643 36117c2b Iustin Pop
  @type  cmd: string or list
644 36117c2b Iustin Pop
  @param cmd: Command to run
645 36117c2b Iustin Pop
  @type env: dict
646 36117c2b Iustin Pop
  @param env: The environment to use
647 36117c2b Iustin Pop
  @type via_shell: bool
648 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
649 36117c2b Iustin Pop
  @type output: str
650 36117c2b Iustin Pop
  @param output: the filename in which to save the output
651 8797df43 Iustin Pop
  @type cwd: string
652 8797df43 Iustin Pop
  @param cwd: the working directory for the program
653 36117c2b Iustin Pop
  @rtype: int
654 36117c2b Iustin Pop
  @return: the exit status
655 36117c2b Iustin Pop

656 36117c2b Iustin Pop
  """
657 36117c2b Iustin Pop
  fh = open(output, "a")
658 36117c2b Iustin Pop
  try:
659 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
660 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
661 36117c2b Iustin Pop
                             stdout=fh,
662 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
663 8797df43 Iustin Pop
                             close_fds=True, env=env,
664 8797df43 Iustin Pop
                             cwd=cwd)
665 36117c2b Iustin Pop
666 36117c2b Iustin Pop
    child.stdin.close()
667 36117c2b Iustin Pop
    status = child.wait()
668 36117c2b Iustin Pop
  finally:
669 36117c2b Iustin Pop
    fh.close()
670 36117c2b Iustin Pop
  return status
671 a8083063 Iustin Pop
672 a8083063 Iustin Pop
673 73027ed2 Michael Hanselmann
def SetCloseOnExecFlag(fd, enable):
674 73027ed2 Michael Hanselmann
  """Sets or unsets the close-on-exec flag on a file descriptor.
675 73027ed2 Michael Hanselmann

676 73027ed2 Michael Hanselmann
  @type fd: int
677 73027ed2 Michael Hanselmann
  @param fd: File descriptor
678 73027ed2 Michael Hanselmann
  @type enable: bool
679 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
680 73027ed2 Michael Hanselmann

681 73027ed2 Michael Hanselmann
  """
682 73027ed2 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
683 73027ed2 Michael Hanselmann
684 73027ed2 Michael Hanselmann
  if enable:
685 73027ed2 Michael Hanselmann
    flags |= fcntl.FD_CLOEXEC
686 73027ed2 Michael Hanselmann
  else:
687 73027ed2 Michael Hanselmann
    flags &= ~fcntl.FD_CLOEXEC
688 73027ed2 Michael Hanselmann
689 73027ed2 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
690 73027ed2 Michael Hanselmann
691 73027ed2 Michael Hanselmann
692 287a1740 Michael Hanselmann
def SetNonblockFlag(fd, enable):
693 287a1740 Michael Hanselmann
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
694 287a1740 Michael Hanselmann

695 287a1740 Michael Hanselmann
  @type fd: int
696 287a1740 Michael Hanselmann
  @param fd: File descriptor
697 287a1740 Michael Hanselmann
  @type enable: bool
698 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
699 287a1740 Michael Hanselmann

700 287a1740 Michael Hanselmann
  """
701 287a1740 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
702 287a1740 Michael Hanselmann
703 287a1740 Michael Hanselmann
  if enable:
704 287a1740 Michael Hanselmann
    flags |= os.O_NONBLOCK
705 287a1740 Michael Hanselmann
  else:
706 287a1740 Michael Hanselmann
    flags &= ~os.O_NONBLOCK
707 287a1740 Michael Hanselmann
708 287a1740 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
709 287a1740 Michael Hanselmann
710 287a1740 Michael Hanselmann
711 edcb5d9e Michael Hanselmann
def RetryOnSignal(fn, *args, **kwargs):
712 edcb5d9e Michael Hanselmann
  """Calls a function again if it failed due to EINTR.
713 edcb5d9e Michael Hanselmann

714 edcb5d9e Michael Hanselmann
  """
715 edcb5d9e Michael Hanselmann
  while True:
716 edcb5d9e Michael Hanselmann
    try:
717 edcb5d9e Michael Hanselmann
      return fn(*args, **kwargs)
718 965d0e5b Guido Trotter
    except EnvironmentError, err:
719 edcb5d9e Michael Hanselmann
      if err.errno != errno.EINTR:
720 edcb5d9e Michael Hanselmann
        raise
721 965d0e5b Guido Trotter
    except (socket.error, select.error), err:
722 965d0e5b Guido Trotter
      # In python 2.6 and above select.error is an IOError, so it's handled
723 965d0e5b Guido Trotter
      # above, in 2.5 and below it's not, and it's handled here.
724 edcb5d9e Michael Hanselmann
      if not (err.args and err.args[0] == errno.EINTR):
725 edcb5d9e Michael Hanselmann
        raise
726 edcb5d9e Michael Hanselmann
727 edcb5d9e Michael Hanselmann
728 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
729 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
730 6bb65e3a Guido Trotter

731 6bb65e3a Guido Trotter
  @type dir_name: string
732 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
733 6bb65e3a Guido Trotter
  @type env: dict
734 6bb65e3a Guido Trotter
  @param env: The environment to use
735 6bb65e3a Guido Trotter
  @type reset_env: boolean
736 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
737 6bb65e3a Guido Trotter
  @rtype: list of tuples
738 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
739 6bb65e3a Guido Trotter

740 6bb65e3a Guido Trotter
  """
741 6bb65e3a Guido Trotter
  rr = []
742 6bb65e3a Guido Trotter
743 6bb65e3a Guido Trotter
  try:
744 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
745 6bb65e3a Guido Trotter
  except OSError, err:
746 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
747 6bb65e3a Guido Trotter
    return rr
748 6bb65e3a Guido Trotter
749 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
750 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
751 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
752 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
753 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
754 6bb65e3a Guido Trotter
    else:
755 6bb65e3a Guido Trotter
      try:
756 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
757 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
758 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
759 6bb65e3a Guido Trotter
      else:
760 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
761 6bb65e3a Guido Trotter
762 6bb65e3a Guido Trotter
  return rr
763 6bb65e3a Guido Trotter
764 6bb65e3a Guido Trotter
765 a8083063 Iustin Pop
def RemoveFile(filename):
766 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
767 a8083063 Iustin Pop

768 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
769 a8083063 Iustin Pop
  errors are passed.
770 a8083063 Iustin Pop

771 58885d79 Iustin Pop
  @type filename: str
772 58885d79 Iustin Pop
  @param filename: the file to be removed
773 58885d79 Iustin Pop

774 a8083063 Iustin Pop
  """
775 a8083063 Iustin Pop
  try:
776 a8083063 Iustin Pop
    os.unlink(filename)
777 a8083063 Iustin Pop
  except OSError, err:
778 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
779 a8083063 Iustin Pop
      raise
780 a8083063 Iustin Pop
781 72087dcd Balazs Lecz
782 72087dcd Balazs Lecz
def RemoveDir(dirname):
783 72087dcd Balazs Lecz
  """Remove an empty directory.
784 72087dcd Balazs Lecz

785 72087dcd Balazs Lecz
  Remove a directory, ignoring non-existing ones.
786 72087dcd Balazs Lecz
  Other errors are passed. This includes the case,
787 72087dcd Balazs Lecz
  where the directory is not empty, so it can't be removed.
788 72087dcd Balazs Lecz

789 72087dcd Balazs Lecz
  @type dirname: str
790 72087dcd Balazs Lecz
  @param dirname: the empty directory to be removed
791 72087dcd Balazs Lecz

792 72087dcd Balazs Lecz
  """
793 72087dcd Balazs Lecz
  try:
794 72087dcd Balazs Lecz
    os.rmdir(dirname)
795 72087dcd Balazs Lecz
  except OSError, err:
796 72087dcd Balazs Lecz
    if err.errno != errno.ENOENT:
797 72087dcd Balazs Lecz
      raise
798 72087dcd Balazs Lecz
799 a8083063 Iustin Pop
800 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
801 6e797216 Michael Hanselmann
  """Renames a file.
802 6e797216 Michael Hanselmann

803 6e797216 Michael Hanselmann
  @type old: string
804 6e797216 Michael Hanselmann
  @param old: Original path
805 6e797216 Michael Hanselmann
  @type new: string
806 6e797216 Michael Hanselmann
  @param new: New path
807 6e797216 Michael Hanselmann
  @type mkdir: bool
808 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
809 6e797216 Michael Hanselmann
  @type mkdir_mode: int
810 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
811 6e797216 Michael Hanselmann

812 6e797216 Michael Hanselmann
  """
813 6e797216 Michael Hanselmann
  try:
814 6e797216 Michael Hanselmann
    return os.rename(old, new)
815 6e797216 Michael Hanselmann
  except OSError, err:
816 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
817 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
818 6e797216 Michael Hanselmann
    # as efficient.
819 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
820 6e797216 Michael Hanselmann
      # Create directory and try again
821 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
822 a426508d Michael Hanselmann
823 6e797216 Michael Hanselmann
      return os.rename(old, new)
824 a426508d Michael Hanselmann
825 6e797216 Michael Hanselmann
    raise
826 6e797216 Michael Hanselmann
827 6e797216 Michael Hanselmann
828 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
829 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
830 76e5f8b5 Michael Hanselmann

831 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
832 76e5f8b5 Michael Hanselmann
  before Python 2.5.
833 76e5f8b5 Michael Hanselmann

834 76e5f8b5 Michael Hanselmann
  """
835 76e5f8b5 Michael Hanselmann
  try:
836 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
837 76e5f8b5 Michael Hanselmann
  except OSError, err:
838 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
839 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
840 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
841 76e5f8b5 Michael Hanselmann
      raise
842 76e5f8b5 Michael Hanselmann
843 76e5f8b5 Michael Hanselmann
844 055f822b Michael Hanselmann
def ResetTempfileModule():
845 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
846 055f822b Michael Hanselmann

847 055f822b Michael Hanselmann
  This function should be called after C{os.fork} in the child process to
848 055f822b Michael Hanselmann
  ensure it creates a newly seeded random generator. Otherwise it would
849 055f822b Michael Hanselmann
  generate the same random parts as the parent process. If several processes
850 055f822b Michael Hanselmann
  race for the creation of a temporary file, this could lead to one not getting
851 055f822b Michael Hanselmann
  a temporary name.
852 055f822b Michael Hanselmann

853 055f822b Michael Hanselmann
  """
854 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
855 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
856 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
857 055f822b Michael Hanselmann
    try:
858 055f822b Michael Hanselmann
      # Reset random name generator
859 055f822b Michael Hanselmann
      tempfile._name_sequence = None
860 055f822b Michael Hanselmann
    finally:
861 055f822b Michael Hanselmann
      tempfile._once_lock.release()
862 055f822b Michael Hanselmann
  else:
863 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
864 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
865 055f822b Michael Hanselmann
866 055f822b Michael Hanselmann
867 a8083063 Iustin Pop
def _FingerprintFile(filename):
868 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
869 a8083063 Iustin Pop

870 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
871 a8083063 Iustin Pop
  instead.
872 a8083063 Iustin Pop

873 58885d79 Iustin Pop
  @type filename: str
874 58885d79 Iustin Pop
  @param filename: the filename to checksum
875 58885d79 Iustin Pop
  @rtype: str
876 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
877 58885d79 Iustin Pop
      of the file
878 a8083063 Iustin Pop

879 a8083063 Iustin Pop
  """
880 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
881 a8083063 Iustin Pop
    return None
882 a8083063 Iustin Pop
883 a8083063 Iustin Pop
  f = open(filename)
884 a8083063 Iustin Pop
885 716a32cb Guido Trotter
  fp = compat.sha1_hash()
886 a8083063 Iustin Pop
  while True:
887 a8083063 Iustin Pop
    data = f.read(4096)
888 a8083063 Iustin Pop
    if not data:
889 a8083063 Iustin Pop
      break
890 a8083063 Iustin Pop
891 a8083063 Iustin Pop
    fp.update(data)
892 a8083063 Iustin Pop
893 a8083063 Iustin Pop
  return fp.hexdigest()
894 a8083063 Iustin Pop
895 a8083063 Iustin Pop
896 a8083063 Iustin Pop
def FingerprintFiles(files):
897 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
898 a8083063 Iustin Pop

899 58885d79 Iustin Pop
  @type files: list
900 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
901 58885d79 Iustin Pop
  @rtype: dict
902 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
903 58885d79 Iustin Pop
      existing files
904 a8083063 Iustin Pop

905 a8083063 Iustin Pop
  """
906 a8083063 Iustin Pop
  ret = {}
907 a8083063 Iustin Pop
908 a8083063 Iustin Pop
  for filename in files:
909 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
910 a8083063 Iustin Pop
    if cksum:
911 a8083063 Iustin Pop
      ret[filename] = cksum
912 a8083063 Iustin Pop
913 a8083063 Iustin Pop
  return ret
914 a8083063 Iustin Pop
915 a8083063 Iustin Pop
916 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
917 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
918 a5728081 Guido Trotter

919 a5728081 Guido Trotter
  @type target: dict
920 a5728081 Guido Trotter
  @param target: the dict to update
921 a5728081 Guido Trotter
  @type key_types: dict
922 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
923 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
924 a5728081 Guido Trotter
  @type allowed_values: list
925 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
926 a5728081 Guido Trotter

927 a5728081 Guido Trotter
  """
928 a5728081 Guido Trotter
  if allowed_values is None:
929 a5728081 Guido Trotter
    allowed_values = []
930 a5728081 Guido Trotter
931 8b46606c Guido Trotter
  if not isinstance(target, dict):
932 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
933 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
934 8b46606c Guido Trotter
935 a5728081 Guido Trotter
  for key in target:
936 a5728081 Guido Trotter
    if key not in key_types:
937 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
938 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
939 a5728081 Guido Trotter
940 a5728081 Guido Trotter
    if target[key] in allowed_values:
941 a5728081 Guido Trotter
      continue
942 a5728081 Guido Trotter
943 29921401 Iustin Pop
    ktype = key_types[key]
944 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
945 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
946 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
947 a5728081 Guido Trotter
948 59525e1f Michael Hanselmann
    if ktype in (constants.VTYPE_STRING, constants.VTYPE_MAYBE_STRING):
949 59525e1f Michael Hanselmann
      if target[key] is None and ktype == constants.VTYPE_MAYBE_STRING:
950 59525e1f Michael Hanselmann
        pass
951 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
952 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
953 a5728081 Guido Trotter
          target[key] = ''
954 a5728081 Guido Trotter
        else:
955 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
956 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
957 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
958 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
959 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
960 a5728081 Guido Trotter
          target[key] = False
961 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
962 a5728081 Guido Trotter
          target[key] = True
963 a5728081 Guido Trotter
        else:
964 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
965 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
966 a5728081 Guido Trotter
      elif target[key]:
967 a5728081 Guido Trotter
        target[key] = True
968 a5728081 Guido Trotter
      else:
969 a5728081 Guido Trotter
        target[key] = False
970 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
971 a5728081 Guido Trotter
      try:
972 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
973 a5728081 Guido Trotter
      except errors.UnitParseError, err:
974 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
975 a5728081 Guido Trotter
              (key, target[key], err)
976 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
977 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
978 a5728081 Guido Trotter
      try:
979 a5728081 Guido Trotter
        target[key] = int(target[key])
980 a5728081 Guido Trotter
      except (ValueError, TypeError):
981 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
982 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
983 a5728081 Guido Trotter
984 a5728081 Guido Trotter
985 a01b500b Michael Hanselmann
def _GetProcStatusPath(pid):
986 a01b500b Michael Hanselmann
  """Returns the path for a PID's proc status file.
987 a01b500b Michael Hanselmann

988 a01b500b Michael Hanselmann
  @type pid: int
989 a01b500b Michael Hanselmann
  @param pid: Process ID
990 a01b500b Michael Hanselmann
  @rtype: string
991 a01b500b Michael Hanselmann

992 a01b500b Michael Hanselmann
  """
993 a01b500b Michael Hanselmann
  return "/proc/%d/status" % pid
994 a01b500b Michael Hanselmann
995 a01b500b Michael Hanselmann
996 a8083063 Iustin Pop
def IsProcessAlive(pid):
997 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
998 a8083063 Iustin Pop

999 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
1000 44bf25ff Iustin Pop
      will be returned as alive
1001 58885d79 Iustin Pop
  @type pid: int
1002 58885d79 Iustin Pop
  @param pid: the process ID to check
1003 58885d79 Iustin Pop
  @rtype: boolean
1004 58885d79 Iustin Pop
  @return: True if the process exists
1005 a8083063 Iustin Pop

1006 a8083063 Iustin Pop
  """
1007 5ef5ea45 Guido Trotter
  def _TryStat(name):
1008 5ef5ea45 Guido Trotter
    try:
1009 5ef5ea45 Guido Trotter
      os.stat(name)
1010 5ef5ea45 Guido Trotter
      return True
1011 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
1012 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
1013 5ef5ea45 Guido Trotter
        return False
1014 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
1015 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
1016 5ef5ea45 Guido Trotter
      raise
1017 5ef5ea45 Guido Trotter
1018 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
1019 d9f311d7 Iustin Pop
  if pid <= 0:
1020 d9f311d7 Iustin Pop
    return False
1021 d9f311d7 Iustin Pop
1022 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
1023 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
1024 a8083063 Iustin Pop
  try:
1025 a01b500b Michael Hanselmann
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
1026 a01b500b Michael Hanselmann
                 args=[_GetProcStatusPath(pid)])
1027 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
1028 5ef5ea45 Guido Trotter
    err.RaiseInner()
1029 a8083063 Iustin Pop
1030 a8083063 Iustin Pop
1031 a01b500b Michael Hanselmann
def _ParseSigsetT(sigset):
1032 a01b500b Michael Hanselmann
  """Parse a rendered sigset_t value.
1033 a01b500b Michael Hanselmann

1034 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
1035 a01b500b Michael Hanselmann
  function.
1036 a01b500b Michael Hanselmann

1037 a01b500b Michael Hanselmann
  @type sigset: string
1038 a01b500b Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
1039 a01b500b Michael Hanselmann
  @rtype: set
1040 a01b500b Michael Hanselmann
  @return: Set of all enabled signal numbers
1041 a01b500b Michael Hanselmann

1042 a01b500b Michael Hanselmann
  """
1043 a01b500b Michael Hanselmann
  result = set()
1044 a01b500b Michael Hanselmann
1045 a01b500b Michael Hanselmann
  signum = 0
1046 a01b500b Michael Hanselmann
  for ch in reversed(sigset):
1047 a01b500b Michael Hanselmann
    chv = int(ch, 16)
1048 a01b500b Michael Hanselmann
1049 a01b500b Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
1050 a01b500b Michael Hanselmann
    # understand in the unrolled form
1051 a01b500b Michael Hanselmann
    if chv & 1:
1052 a01b500b Michael Hanselmann
      result.add(signum + 1)
1053 a01b500b Michael Hanselmann
    if chv & 2:
1054 a01b500b Michael Hanselmann
      result.add(signum + 2)
1055 a01b500b Michael Hanselmann
    if chv & 4:
1056 a01b500b Michael Hanselmann
      result.add(signum + 3)
1057 a01b500b Michael Hanselmann
    if chv & 8:
1058 a01b500b Michael Hanselmann
      result.add(signum + 4)
1059 a01b500b Michael Hanselmann
1060 a01b500b Michael Hanselmann
    signum += 4
1061 a01b500b Michael Hanselmann
1062 a01b500b Michael Hanselmann
  return result
1063 a01b500b Michael Hanselmann
1064 a01b500b Michael Hanselmann
1065 a01b500b Michael Hanselmann
def _GetProcStatusField(pstatus, field):
1066 a01b500b Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
1067 a01b500b Michael Hanselmann

1068 a01b500b Michael Hanselmann
  @type pstatus: string
1069 a01b500b Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
1070 a01b500b Michael Hanselmann
  @type field: string
1071 a01b500b Michael Hanselmann
  @param field: Name of field whose value should be returned
1072 a01b500b Michael Hanselmann
  @rtype: string
1073 a01b500b Michael Hanselmann

1074 a01b500b Michael Hanselmann
  """
1075 a01b500b Michael Hanselmann
  for line in pstatus.splitlines():
1076 a01b500b Michael Hanselmann
    parts = line.split(":", 1)
1077 a01b500b Michael Hanselmann
1078 a01b500b Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
1079 a01b500b Michael Hanselmann
      continue
1080 a01b500b Michael Hanselmann
1081 a01b500b Michael Hanselmann
    return parts[1].strip()
1082 a01b500b Michael Hanselmann
1083 a01b500b Michael Hanselmann
  return None
1084 a01b500b Michael Hanselmann
1085 a01b500b Michael Hanselmann
1086 a01b500b Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
1087 a01b500b Michael Hanselmann
  """Checks whether a process is handling a signal.
1088 a01b500b Michael Hanselmann

1089 a01b500b Michael Hanselmann
  @type pid: int
1090 a01b500b Michael Hanselmann
  @param pid: Process ID
1091 a01b500b Michael Hanselmann
  @type signum: int
1092 a01b500b Michael Hanselmann
  @param signum: Signal number
1093 a01b500b Michael Hanselmann
  @rtype: bool
1094 a01b500b Michael Hanselmann

1095 a01b500b Michael Hanselmann
  """
1096 a01b500b Michael Hanselmann
  if status_path is None:
1097 a01b500b Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
1098 a01b500b Michael Hanselmann
1099 a01b500b Michael Hanselmann
  try:
1100 a01b500b Michael Hanselmann
    proc_status = ReadFile(status_path)
1101 a01b500b Michael Hanselmann
  except EnvironmentError, err:
1102 a01b500b Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
1103 a01b500b Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
1104 a01b500b Michael Hanselmann
      return False
1105 a01b500b Michael Hanselmann
    raise
1106 a01b500b Michael Hanselmann
1107 a01b500b Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
1108 a01b500b Michael Hanselmann
  if sigcgt is None:
1109 a01b500b Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
1110 a01b500b Michael Hanselmann
1111 a01b500b Michael Hanselmann
  # Now check whether signal is handled
1112 a01b500b Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
1113 a01b500b Michael Hanselmann
1114 a01b500b Michael Hanselmann
1115 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
1116 58885d79 Iustin Pop
  """Read a pid from a file.
1117 fee80e90 Guido Trotter

1118 58885d79 Iustin Pop
  @type  pidfile: string
1119 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
1120 58885d79 Iustin Pop
  @rtype: int
1121 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
1122 d9f311d7 Iustin Pop
           otherwise 0
1123 fee80e90 Guido Trotter

1124 fee80e90 Guido Trotter
  """
1125 fee80e90 Guido Trotter
  try:
1126 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
1127 d9f311d7 Iustin Pop
  except EnvironmentError, err:
1128 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
1129 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
1130 d9f311d7 Iustin Pop
    return 0
1131 fee80e90 Guido Trotter
1132 fee80e90 Guido Trotter
  try:
1133 13998ef2 Michael Hanselmann
    pid = int(raw_data)
1134 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
1135 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
1136 d9f311d7 Iustin Pop
    return 0
1137 fee80e90 Guido Trotter
1138 d9f311d7 Iustin Pop
  return pid
1139 fee80e90 Guido Trotter
1140 fee80e90 Guido Trotter
1141 debed9ae Michael Hanselmann
def ReadLockedPidFile(path):
1142 debed9ae Michael Hanselmann
  """Reads a locked PID file.
1143 debed9ae Michael Hanselmann

1144 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
1145 debed9ae Michael Hanselmann

1146 debed9ae Michael Hanselmann
  @type path: string
1147 debed9ae Michael Hanselmann
  @param path: Path to PID file
1148 debed9ae Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
1149 debed9ae Michael Hanselmann

1150 debed9ae Michael Hanselmann
  """
1151 debed9ae Michael Hanselmann
  try:
1152 debed9ae Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
1153 debed9ae Michael Hanselmann
  except EnvironmentError, err:
1154 debed9ae Michael Hanselmann
    if err.errno == errno.ENOENT:
1155 debed9ae Michael Hanselmann
      # PID file doesn't exist
1156 debed9ae Michael Hanselmann
      return None
1157 debed9ae Michael Hanselmann
    raise
1158 debed9ae Michael Hanselmann
1159 debed9ae Michael Hanselmann
  try:
1160 debed9ae Michael Hanselmann
    try:
1161 debed9ae Michael Hanselmann
      # Try to acquire lock
1162 debed9ae Michael Hanselmann
      LockFile(fd)
1163 debed9ae Michael Hanselmann
    except errors.LockError:
1164 debed9ae Michael Hanselmann
      # Couldn't lock, daemon is running
1165 debed9ae Michael Hanselmann
      return int(os.read(fd, 100))
1166 debed9ae Michael Hanselmann
  finally:
1167 debed9ae Michael Hanselmann
    os.close(fd)
1168 debed9ae Michael Hanselmann
1169 debed9ae Michael Hanselmann
  return None
1170 debed9ae Michael Hanselmann
1171 debed9ae Michael Hanselmann
1172 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
1173 a8083063 Iustin Pop
  """Try to match a name against a list.
1174 a8083063 Iustin Pop

1175 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
1176 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
1177 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
1178 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
1179 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
1180 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
1181 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
1182 a8083063 Iustin Pop

1183 58885d79 Iustin Pop
  @type key: str
1184 58885d79 Iustin Pop
  @param key: the name to be searched
1185 58885d79 Iustin Pop
  @type name_list: list
1186 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
1187 256eb94b Guido Trotter
  @type case_sensitive: boolean
1188 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
1189 a8083063 Iustin Pop

1190 58885d79 Iustin Pop
  @rtype: None or str
1191 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
1192 58885d79 Iustin Pop
      otherwise the element from the list which matches
1193 a8083063 Iustin Pop

1194 a8083063 Iustin Pop
  """
1195 3a541d90 Iustin Pop
  if key in name_list:
1196 3a541d90 Iustin Pop
    return key
1197 256eb94b Guido Trotter
1198 256eb94b Guido Trotter
  re_flags = 0
1199 256eb94b Guido Trotter
  if not case_sensitive:
1200 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
1201 099c52ad Iustin Pop
    key = key.upper()
1202 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
1203 256eb94b Guido Trotter
  names_filtered = []
1204 256eb94b Guido Trotter
  string_matches = []
1205 256eb94b Guido Trotter
  for name in name_list:
1206 256eb94b Guido Trotter
    if mo.match(name) is not None:
1207 256eb94b Guido Trotter
      names_filtered.append(name)
1208 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
1209 256eb94b Guido Trotter
        string_matches.append(name)
1210 256eb94b Guido Trotter
1211 256eb94b Guido Trotter
  if len(string_matches) == 1:
1212 256eb94b Guido Trotter
    return string_matches[0]
1213 256eb94b Guido Trotter
  if len(names_filtered) == 1:
1214 256eb94b Guido Trotter
    return names_filtered[0]
1215 256eb94b Guido Trotter
  return None
1216 a8083063 Iustin Pop
1217 a8083063 Iustin Pop
1218 28f34048 Michael Hanselmann
def ValidateServiceName(name):
1219 28f34048 Michael Hanselmann
  """Validate the given service name.
1220 28f34048 Michael Hanselmann

1221 28f34048 Michael Hanselmann
  @type name: number or string
1222 28f34048 Michael Hanselmann
  @param name: Service name or port specification
1223 28f34048 Michael Hanselmann

1224 28f34048 Michael Hanselmann
  """
1225 28f34048 Michael Hanselmann
  try:
1226 28f34048 Michael Hanselmann
    numport = int(name)
1227 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
1228 28f34048 Michael Hanselmann
    # Non-numeric service name
1229 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
1230 28f34048 Michael Hanselmann
  else:
1231 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
1232 28f34048 Michael Hanselmann
    # here)
1233 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
1234 28f34048 Michael Hanselmann
1235 28f34048 Michael Hanselmann
  if not valid:
1236 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
1237 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
1238 28f34048 Michael Hanselmann
1239 28f34048 Michael Hanselmann
  return name
1240 28f34048 Michael Hanselmann
1241 28f34048 Michael Hanselmann
1242 a8083063 Iustin Pop
def ListVolumeGroups():
1243 a8083063 Iustin Pop
  """List volume groups and their size
1244 a8083063 Iustin Pop

1245 58885d79 Iustin Pop
  @rtype: dict
1246 58885d79 Iustin Pop
  @return:
1247 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1248 58885d79 Iustin Pop
       the size of the volume
1249 a8083063 Iustin Pop

1250 a8083063 Iustin Pop
  """
1251 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
1252 a8083063 Iustin Pop
  result = RunCmd(command)
1253 a8083063 Iustin Pop
  retval = {}
1254 a8083063 Iustin Pop
  if result.failed:
1255 a8083063 Iustin Pop
    return retval
1256 a8083063 Iustin Pop
1257 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
1258 a8083063 Iustin Pop
    try:
1259 a8083063 Iustin Pop
      name, size = line.split()
1260 a8083063 Iustin Pop
      size = int(float(size))
1261 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
1262 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
1263 a8083063 Iustin Pop
      continue
1264 a8083063 Iustin Pop
1265 a8083063 Iustin Pop
    retval[name] = size
1266 a8083063 Iustin Pop
1267 a8083063 Iustin Pop
  return retval
1268 a8083063 Iustin Pop
1269 a8083063 Iustin Pop
1270 a8083063 Iustin Pop
def BridgeExists(bridge):
1271 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
1272 a8083063 Iustin Pop

1273 58885d79 Iustin Pop
  @type bridge: str
1274 58885d79 Iustin Pop
  @param bridge: the bridge name to check
1275 58885d79 Iustin Pop
  @rtype: boolean
1276 58885d79 Iustin Pop
  @return: True if it does
1277 a8083063 Iustin Pop

1278 a8083063 Iustin Pop
  """
1279 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
1280 a8083063 Iustin Pop
1281 a8083063 Iustin Pop
1282 a8083063 Iustin Pop
def NiceSort(name_list):
1283 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
1284 a8083063 Iustin Pop

1285 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
1286 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
1287 58885d79 Iustin Pop
  'a11']}.
1288 a8083063 Iustin Pop

1289 a8083063 Iustin Pop
  The sort algorithm breaks each name in groups of either only-digits
1290 a8083063 Iustin Pop
  or no-digits. Only the first eight such groups are considered, and
1291 a8083063 Iustin Pop
  after that we just use what's left of the string.
1292 a8083063 Iustin Pop

1293 58885d79 Iustin Pop
  @type name_list: list
1294 58885d79 Iustin Pop
  @param name_list: the names to be sorted
1295 58885d79 Iustin Pop
  @rtype: list
1296 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
1297 a8083063 Iustin Pop

1298 a8083063 Iustin Pop
  """
1299 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
1300 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
1301 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1302 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1303 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
1304 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
1305 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
1306 a8083063 Iustin Pop
  def _TryInt(val):
1307 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
1308 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
1309 a8083063 Iustin Pop
      return val
1310 a8083063 Iustin Pop
    rval = int(val)
1311 a8083063 Iustin Pop
    return rval
1312 a8083063 Iustin Pop
1313 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
1314 a8083063 Iustin Pop
             for name in name_list]
1315 a8083063 Iustin Pop
  to_sort.sort()
1316 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
1317 a8083063 Iustin Pop
1318 a8083063 Iustin Pop
1319 a8083063 Iustin Pop
def TryConvert(fn, val):
1320 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
1321 a8083063 Iustin Pop

1322 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
1323 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
1324 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
1325 58885d79 Iustin Pop
  exceptions are propagated to the caller.
1326 58885d79 Iustin Pop

1327 58885d79 Iustin Pop
  @type fn: callable
1328 58885d79 Iustin Pop
  @param fn: function to apply to the value
1329 58885d79 Iustin Pop
  @param val: the value to be converted
1330 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
1331 58885d79 Iustin Pop
      otherwise the original value.
1332 a8083063 Iustin Pop

1333 a8083063 Iustin Pop
  """
1334 a8083063 Iustin Pop
  try:
1335 a8083063 Iustin Pop
    nv = fn(val)
1336 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
1337 a8083063 Iustin Pop
    nv = val
1338 a8083063 Iustin Pop
  return nv
1339 a8083063 Iustin Pop
1340 a8083063 Iustin Pop
1341 a8083063 Iustin Pop
def IsValidShellParam(word):
1342 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1343 a8083063 Iustin Pop

1344 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1345 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1346 a8083063 Iustin Pop
  the actual command.
1347 a8083063 Iustin Pop

1348 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1349 a8083063 Iustin Pop
  side.
1350 a8083063 Iustin Pop

1351 58885d79 Iustin Pop
  @type word: str
1352 58885d79 Iustin Pop
  @param word: the word to check
1353 58885d79 Iustin Pop
  @rtype: boolean
1354 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1355 58885d79 Iustin Pop

1356 a8083063 Iustin Pop
  """
1357 0b5303da Iustin Pop
  return bool(_SHELLPARAM_REGEX.match(word))
1358 a8083063 Iustin Pop
1359 a8083063 Iustin Pop
1360 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1361 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1362 a8083063 Iustin Pop

1363 a8083063 Iustin Pop
  This function will check all arguments in the args list so that they
1364 a8083063 Iustin Pop
  are valid shell parameters (i.e. they don't contain shell
1365 5bbd3f7f Michael Hanselmann
  metacharacters). If everything is ok, it will return the result of
1366 a8083063 Iustin Pop
  template % args.
1367 a8083063 Iustin Pop

1368 58885d79 Iustin Pop
  @type template: str
1369 58885d79 Iustin Pop
  @param template: the string holding the template for the
1370 58885d79 Iustin Pop
      string formatting
1371 58885d79 Iustin Pop
  @rtype: str
1372 58885d79 Iustin Pop
  @return: the expanded command line
1373 58885d79 Iustin Pop

1374 a8083063 Iustin Pop
  """
1375 a8083063 Iustin Pop
  for word in args:
1376 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1377 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1378 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1379 a8083063 Iustin Pop
  return template % args
1380 a8083063 Iustin Pop
1381 a8083063 Iustin Pop
1382 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
1383 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
1384 a8083063 Iustin Pop

1385 58885d79 Iustin Pop
  @type value: int
1386 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
1387 9fbfbb7b Iustin Pop
  @type units: char
1388 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
1389 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
1390 9fbfbb7b Iustin Pop
      - 'm' for MiBs
1391 9fbfbb7b Iustin Pop
      - 'g' for GiBs
1392 9fbfbb7b Iustin Pop
      - 't' for TiBs
1393 58885d79 Iustin Pop
  @rtype: str
1394 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
1395 a8083063 Iustin Pop

1396 a8083063 Iustin Pop
  """
1397 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
1398 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
1399 a8083063 Iustin Pop
1400 9fbfbb7b Iustin Pop
  suffix = ''
1401 9fbfbb7b Iustin Pop
1402 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
1403 9fbfbb7b Iustin Pop
    if units == 'h':
1404 9fbfbb7b Iustin Pop
      suffix = 'M'
1405 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
1406 9fbfbb7b Iustin Pop
1407 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
1408 9fbfbb7b Iustin Pop
    if units == 'h':
1409 9fbfbb7b Iustin Pop
      suffix = 'G'
1410 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
1411 a8083063 Iustin Pop
1412 a8083063 Iustin Pop
  else:
1413 9fbfbb7b Iustin Pop
    if units == 'h':
1414 9fbfbb7b Iustin Pop
      suffix = 'T'
1415 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
1416 a8083063 Iustin Pop
1417 a8083063 Iustin Pop
1418 a8083063 Iustin Pop
def ParseUnit(input_string):
1419 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
1420 a8083063 Iustin Pop

1421 58885d79 Iustin Pop
  Input must be in the format C{NUMBER+ [DOT NUMBER+] SPACE*
1422 58885d79 Iustin Pop
  [UNIT]}. If no unit is specified, it defaults to MiB. Return value
1423 58885d79 Iustin Pop
  is always an int in MiB.
1424 a8083063 Iustin Pop

1425 a8083063 Iustin Pop
  """
1426 0b5303da Iustin Pop
  m = _PARSEUNIT_REGEX.match(str(input_string))
1427 a8083063 Iustin Pop
  if not m:
1428 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
1429 a8083063 Iustin Pop
1430 a8083063 Iustin Pop
  value = float(m.groups()[0])
1431 a8083063 Iustin Pop
1432 a8083063 Iustin Pop
  unit = m.groups()[1]
1433 a8083063 Iustin Pop
  if unit:
1434 a8083063 Iustin Pop
    lcunit = unit.lower()
1435 a8083063 Iustin Pop
  else:
1436 a8083063 Iustin Pop
    lcunit = 'm'
1437 a8083063 Iustin Pop
1438 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
1439 a8083063 Iustin Pop
    # Value already in MiB
1440 a8083063 Iustin Pop
    pass
1441 a8083063 Iustin Pop
1442 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
1443 a8083063 Iustin Pop
    value *= 1024
1444 a8083063 Iustin Pop
1445 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
1446 a8083063 Iustin Pop
    value *= 1024 * 1024
1447 a8083063 Iustin Pop
1448 a8083063 Iustin Pop
  else:
1449 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
1450 a8083063 Iustin Pop
1451 a8083063 Iustin Pop
  # Make sure we round up
1452 a8083063 Iustin Pop
  if int(value) < value:
1453 a8083063 Iustin Pop
    value += 1
1454 a8083063 Iustin Pop
1455 a8083063 Iustin Pop
  # Round up to the next multiple of 4
1456 a8083063 Iustin Pop
  value = int(value)
1457 a8083063 Iustin Pop
  if value % 4:
1458 a8083063 Iustin Pop
    value += 4 - value % 4
1459 a8083063 Iustin Pop
1460 a8083063 Iustin Pop
  return value
1461 a8083063 Iustin Pop
1462 a8083063 Iustin Pop
1463 31155d60 Balazs Lecz
def ParseCpuMask(cpu_mask):
1464 31155d60 Balazs Lecz
  """Parse a CPU mask definition and return the list of CPU IDs.
1465 31155d60 Balazs Lecz

1466 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
1467 31155d60 Balazs Lecz
  or dash-separated ID ranges
1468 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
1469 31155d60 Balazs Lecz

1470 31155d60 Balazs Lecz
  @type cpu_mask: str
1471 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1472 31155d60 Balazs Lecz
  @rtype: list of int
1473 31155d60 Balazs Lecz
  @return: list of CPU IDs
1474 31155d60 Balazs Lecz

1475 31155d60 Balazs Lecz
  """
1476 31155d60 Balazs Lecz
  if not cpu_mask:
1477 31155d60 Balazs Lecz
    return []
1478 31155d60 Balazs Lecz
  cpu_list = []
1479 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
1480 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
1481 31155d60 Balazs Lecz
    n_elements = len(boundaries)
1482 31155d60 Balazs Lecz
    if n_elements > 2:
1483 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1484 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
1485 31155d60 Balazs Lecz
    try:
1486 31155d60 Balazs Lecz
      lower = int(boundaries[0])
1487 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1488 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
1489 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1490 31155d60 Balazs Lecz
    try:
1491 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
1492 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1493 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
1494 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1495 31155d60 Balazs Lecz
    if lower > higher:
1496 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1497 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
1498 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
1499 31155d60 Balazs Lecz
  return cpu_list
1500 31155d60 Balazs Lecz
1501 31155d60 Balazs Lecz
1502 3727671e René Nussbaumer
def AddAuthorizedKey(file_obj, key):
1503 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1504 a8083063 Iustin Pop

1505 3727671e René Nussbaumer
  @type file_obj: str or file handle
1506 3727671e René Nussbaumer
  @param file_obj: path to authorized_keys file
1507 58885d79 Iustin Pop
  @type key: str
1508 58885d79 Iustin Pop
  @param key: string containing key
1509 58885d79 Iustin Pop

1510 a8083063 Iustin Pop
  """
1511 a8083063 Iustin Pop
  key_fields = key.split()
1512 a8083063 Iustin Pop
1513 3727671e René Nussbaumer
  if isinstance(file_obj, basestring):
1514 3727671e René Nussbaumer
    f = open(file_obj, 'a+')
1515 3727671e René Nussbaumer
  else:
1516 3727671e René Nussbaumer
    f = file_obj
1517 3727671e René Nussbaumer
1518 a8083063 Iustin Pop
  try:
1519 a8083063 Iustin Pop
    nl = True
1520 a8083063 Iustin Pop
    for line in f:
1521 a8083063 Iustin Pop
      # Ignore whitespace changes
1522 a8083063 Iustin Pop
      if line.split() == key_fields:
1523 a8083063 Iustin Pop
        break
1524 a8083063 Iustin Pop
      nl = line.endswith('\n')
1525 a8083063 Iustin Pop
    else:
1526 a8083063 Iustin Pop
      if not nl:
1527 a8083063 Iustin Pop
        f.write("\n")
1528 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1529 a8083063 Iustin Pop
      f.write("\n")
1530 a8083063 Iustin Pop
      f.flush()
1531 a8083063 Iustin Pop
  finally:
1532 a8083063 Iustin Pop
    f.close()
1533 a8083063 Iustin Pop
1534 a8083063 Iustin Pop
1535 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1536 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1537 a8083063 Iustin Pop

1538 58885d79 Iustin Pop
  @type file_name: str
1539 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1540 58885d79 Iustin Pop
  @type key: str
1541 58885d79 Iustin Pop
  @param key: string containing key
1542 58885d79 Iustin Pop

1543 a8083063 Iustin Pop
  """
1544 a8083063 Iustin Pop
  key_fields = key.split()
1545 a8083063 Iustin Pop
1546 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1547 a8083063 Iustin Pop
  try:
1548 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1549 a8083063 Iustin Pop
    try:
1550 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1551 59f82e3f Michael Hanselmann
      try:
1552 59f82e3f Michael Hanselmann
        for line in f:
1553 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1554 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1555 59f82e3f Michael Hanselmann
            out.write(line)
1556 899d2a81 Michael Hanselmann
1557 899d2a81 Michael Hanselmann
        out.flush()
1558 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1559 899d2a81 Michael Hanselmann
      finally:
1560 899d2a81 Michael Hanselmann
        f.close()
1561 899d2a81 Michael Hanselmann
    finally:
1562 899d2a81 Michael Hanselmann
      out.close()
1563 899d2a81 Michael Hanselmann
  except:
1564 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1565 899d2a81 Michael Hanselmann
    raise
1566 899d2a81 Michael Hanselmann
1567 899d2a81 Michael Hanselmann
1568 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1569 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1570 899d2a81 Michael Hanselmann

1571 58885d79 Iustin Pop
  @type file_name: str
1572 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1573 58885d79 Iustin Pop
  @type ip: str
1574 58885d79 Iustin Pop
  @param ip: the IP address
1575 58885d79 Iustin Pop
  @type hostname: str
1576 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1577 58885d79 Iustin Pop
  @type aliases: list
1578 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1579 58885d79 Iustin Pop

1580 899d2a81 Michael Hanselmann
  """
1581 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1582 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1583 7fbb1f65 Michael Hanselmann
1584 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1585 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1586 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1587 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1588 9440aeab Michael Hanselmann
    try:
1589 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1590 edcd876b Michael Hanselmann
        fields = line.split()
1591 edcd876b Michael Hanselmann
        if fields and not fields[0].startswith("#") and ip == fields[0]:
1592 edcd876b Michael Hanselmann
          continue
1593 edcd876b Michael Hanselmann
        out.write(line)
1594 edcd876b Michael Hanselmann
1595 edcd876b Michael Hanselmann
      out.write("%s\t%s" % (ip, hostname))
1596 edcd876b Michael Hanselmann
      if aliases:
1597 edcd876b Michael Hanselmann
        out.write(" %s" % " ".join(aliases))
1598 edcd876b Michael Hanselmann
      out.write("\n")
1599 edcd876b Michael Hanselmann
      out.flush()
1600 9440aeab Michael Hanselmann
    finally:
1601 9440aeab Michael Hanselmann
      out.close()
1602 edcd876b Michael Hanselmann
1603 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1604 899d2a81 Michael Hanselmann
1605 899d2a81 Michael Hanselmann
1606 ea8ac9c9 René Nussbaumer
def AddHostToEtcHosts(hostname, ip):
1607 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1608 d9c02ca6 Michael Hanselmann

1609 58885d79 Iustin Pop
  @type hostname: str
1610 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1611 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1612 ea8ac9c9 René Nussbaumer
  @type ip: str
1613 ea8ac9c9 René Nussbaumer
  @param ip: The ip address of the host
1614 58885d79 Iustin Pop

1615 d9c02ca6 Michael Hanselmann
  """
1616 ea8ac9c9 René Nussbaumer
  SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]])
1617 d9c02ca6 Michael Hanselmann
1618 d9c02ca6 Michael Hanselmann
1619 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1620 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1621 899d2a81 Michael Hanselmann

1622 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
1623 58885d79 Iustin Pop

1624 58885d79 Iustin Pop
  @type file_name: str
1625 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1626 58885d79 Iustin Pop
  @type hostname: str
1627 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1628 58885d79 Iustin Pop

1629 899d2a81 Michael Hanselmann
  """
1630 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1631 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1632 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1633 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1634 899d2a81 Michael Hanselmann
    try:
1635 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1636 edcd876b Michael Hanselmann
        fields = line.split()
1637 edcd876b Michael Hanselmann
        if len(fields) > 1 and not fields[0].startswith("#"):
1638 edcd876b Michael Hanselmann
          names = fields[1:]
1639 edcd876b Michael Hanselmann
          if hostname in names:
1640 edcd876b Michael Hanselmann
            while hostname in names:
1641 edcd876b Michael Hanselmann
              names.remove(hostname)
1642 edcd876b Michael Hanselmann
            if names:
1643 edcd876b Michael Hanselmann
              out.write("%s %s\n" % (fields[0], " ".join(names)))
1644 edcd876b Michael Hanselmann
            continue
1645 59f82e3f Michael Hanselmann
1646 edcd876b Michael Hanselmann
        out.write(line)
1647 edcd876b Michael Hanselmann
1648 edcd876b Michael Hanselmann
      out.flush()
1649 a8083063 Iustin Pop
    finally:
1650 59f82e3f Michael Hanselmann
      out.close()
1651 edcd876b Michael Hanselmann
1652 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1653 a8083063 Iustin Pop
1654 a8083063 Iustin Pop
1655 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1656 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1657 d9c02ca6 Michael Hanselmann

1658 58885d79 Iustin Pop
  @type hostname: str
1659 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1660 58885d79 Iustin Pop
      full and shot name will be removed from
1661 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1662 58885d79 Iustin Pop

1663 d9c02ca6 Michael Hanselmann
  """
1664 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname)
1665 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0])
1666 d9c02ca6 Michael Hanselmann
1667 d9c02ca6 Michael Hanselmann
1668 1d466a4f Michael Hanselmann
def TimestampForFilename():
1669 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1670 1d466a4f Michael Hanselmann

1671 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1672 1d466a4f Michael Hanselmann
  separators.
1673 1d466a4f Michael Hanselmann

1674 1d466a4f Michael Hanselmann
  """
1675 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1676 1d466a4f Michael Hanselmann
1677 1d466a4f Michael Hanselmann
1678 a8083063 Iustin Pop
def CreateBackup(file_name):
1679 a8083063 Iustin Pop
  """Creates a backup of a file.
1680 a8083063 Iustin Pop

1681 58885d79 Iustin Pop
  @type file_name: str
1682 58885d79 Iustin Pop
  @param file_name: file to be backed up
1683 58885d79 Iustin Pop
  @rtype: str
1684 58885d79 Iustin Pop
  @return: the path to the newly created backup
1685 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1686 a8083063 Iustin Pop

1687 a8083063 Iustin Pop
  """
1688 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1689 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1690 3ecf6786 Iustin Pop
                                file_name)
1691 a8083063 Iustin Pop
1692 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1693 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1694 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1695 081b1e69 Michael Hanselmann
1696 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1697 081b1e69 Michael Hanselmann
  try:
1698 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1699 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1700 081b1e69 Michael Hanselmann
    try:
1701 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1702 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1703 081b1e69 Michael Hanselmann
    finally:
1704 081b1e69 Michael Hanselmann
      fdst.close()
1705 081b1e69 Michael Hanselmann
  finally:
1706 081b1e69 Michael Hanselmann
    fsrc.close()
1707 081b1e69 Michael Hanselmann
1708 a8083063 Iustin Pop
  return backup_name
1709 a8083063 Iustin Pop
1710 a8083063 Iustin Pop
1711 a8083063 Iustin Pop
def ShellQuote(value):
1712 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1713 3ecf6786 Iustin Pop

1714 58885d79 Iustin Pop
  @type value: str
1715 58885d79 Iustin Pop
  @param value: the argument to be quoted
1716 58885d79 Iustin Pop
  @rtype: str
1717 58885d79 Iustin Pop
  @return: the quoted value
1718 58885d79 Iustin Pop

1719 a8083063 Iustin Pop
  """
1720 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1721 a8083063 Iustin Pop
    return value
1722 a8083063 Iustin Pop
  else:
1723 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1724 a8083063 Iustin Pop
1725 a8083063 Iustin Pop
1726 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1727 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1728 58885d79 Iustin Pop

1729 58885d79 Iustin Pop
  @type args: list
1730 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1731 58885d79 Iustin Pop
  @rtype: str
1732 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1733 a8083063 Iustin Pop

1734 a8083063 Iustin Pop
  """
1735 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1736 88d14415 Michael Hanselmann
1737 88d14415 Michael Hanselmann
1738 858905fb Michael Hanselmann
class ShellWriter:
1739 858905fb Michael Hanselmann
  """Helper class to write scripts with indentation.
1740 858905fb Michael Hanselmann

1741 858905fb Michael Hanselmann
  """
1742 858905fb Michael Hanselmann
  INDENT_STR = "  "
1743 858905fb Michael Hanselmann
1744 858905fb Michael Hanselmann
  def __init__(self, fh):
1745 858905fb Michael Hanselmann
    """Initializes this class.
1746 858905fb Michael Hanselmann

1747 858905fb Michael Hanselmann
    """
1748 858905fb Michael Hanselmann
    self._fh = fh
1749 858905fb Michael Hanselmann
    self._indent = 0
1750 858905fb Michael Hanselmann
1751 858905fb Michael Hanselmann
  def IncIndent(self):
1752 858905fb Michael Hanselmann
    """Increase indentation level by 1.
1753 858905fb Michael Hanselmann

1754 858905fb Michael Hanselmann
    """
1755 858905fb Michael Hanselmann
    self._indent += 1
1756 858905fb Michael Hanselmann
1757 858905fb Michael Hanselmann
  def DecIndent(self):
1758 858905fb Michael Hanselmann
    """Decrease indentation level by 1.
1759 858905fb Michael Hanselmann

1760 858905fb Michael Hanselmann
    """
1761 858905fb Michael Hanselmann
    assert self._indent > 0
1762 858905fb Michael Hanselmann
    self._indent -= 1
1763 858905fb Michael Hanselmann
1764 858905fb Michael Hanselmann
  def Write(self, txt, *args):
1765 858905fb Michael Hanselmann
    """Write line to output file.
1766 858905fb Michael Hanselmann

1767 858905fb Michael Hanselmann
    """
1768 858905fb Michael Hanselmann
    assert self._indent >= 0
1769 858905fb Michael Hanselmann
1770 858905fb Michael Hanselmann
    self._fh.write(self._indent * self.INDENT_STR)
1771 858905fb Michael Hanselmann
1772 858905fb Michael Hanselmann
    if args:
1773 858905fb Michael Hanselmann
      self._fh.write(txt % args)
1774 858905fb Michael Hanselmann
    else:
1775 858905fb Michael Hanselmann
      self._fh.write(txt)
1776 858905fb Michael Hanselmann
1777 858905fb Michael Hanselmann
    self._fh.write("\n")
1778 858905fb Michael Hanselmann
1779 858905fb Michael Hanselmann
1780 b5b8309d Guido Trotter
def ListVisibleFiles(path):
1781 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1782 58885d79 Iustin Pop

1783 58885d79 Iustin Pop
  @type path: str
1784 58885d79 Iustin Pop
  @param path: the directory to enumerate
1785 58885d79 Iustin Pop
  @rtype: list
1786 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1787 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1788 eedbda4b Michael Hanselmann

1789 eedbda4b Michael Hanselmann
  """
1790 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1791 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1792 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1793 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1794 f3299a07 Michael Hanselmann
  return files
1795 2f8b60b3 Iustin Pop
1796 2f8b60b3 Iustin Pop
1797 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1798 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1799 257f4c0a Iustin Pop

1800 257f4c0a Iustin Pop
  The user can be passed either as a string (denoting the name) or as
1801 257f4c0a Iustin Pop
  an integer (denoting the user id). If the user is not found, the
1802 257f4c0a Iustin Pop
  'default' argument is returned, which defaults to None.
1803 2f8b60b3 Iustin Pop

1804 2f8b60b3 Iustin Pop
  """
1805 2f8b60b3 Iustin Pop
  try:
1806 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1807 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1808 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1809 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1810 257f4c0a Iustin Pop
    else:
1811 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1812 257f4c0a Iustin Pop
                                   type(user))
1813 2f8b60b3 Iustin Pop
  except KeyError:
1814 2f8b60b3 Iustin Pop
    return default
1815 2f8b60b3 Iustin Pop
  return result.pw_dir
1816 59072e7e Michael Hanselmann
1817 59072e7e Michael Hanselmann
1818 24818e8f Michael Hanselmann
def NewUUID():
1819 59072e7e Michael Hanselmann
  """Returns a random UUID.
1820 59072e7e Michael Hanselmann

1821 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1822 58885d79 Iustin Pop
      filesystem.
1823 58885d79 Iustin Pop
  @rtype: str
1824 58885d79 Iustin Pop

1825 59072e7e Michael Hanselmann
  """
1826 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1827 087b34fe Iustin Pop
1828 087b34fe Iustin Pop
1829 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1830 33081d90 Iustin Pop
  """Generates a random secret.
1831 33081d90 Iustin Pop

1832 ec2c2bc4 Luca Bigliardi
  This will generate a pseudo-random secret returning an hex string
1833 33081d90 Iustin Pop
  (so that it can be used where an ASCII string is needed).
1834 33081d90 Iustin Pop

1835 ec2c2bc4 Luca Bigliardi
  @param numbytes: the number of bytes which will be represented by the returned
1836 ec2c2bc4 Luca Bigliardi
      string (defaulting to 20, the length of a SHA1 hash)
1837 58885d79 Iustin Pop
  @rtype: str
1838 ec2c2bc4 Luca Bigliardi
  @return: an hex representation of the pseudo-random sequence
1839 58885d79 Iustin Pop

1840 33081d90 Iustin Pop
  """
1841 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1842 33081d90 Iustin Pop
1843 33081d90 Iustin Pop
1844 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1845 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1846 9dae41ad Guido Trotter

1847 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1848 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1849 9dae41ad Guido Trotter

1850 9dae41ad Guido Trotter
  """
1851 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1852 9dae41ad Guido Trotter
    try:
1853 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1854 9dae41ad Guido Trotter
    except EnvironmentError, err:
1855 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1856 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1857 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1858 b73360e3 Balazs Lecz
    try:
1859 b73360e3 Balazs Lecz
      os.chmod(dir_name, dir_mode)
1860 b73360e3 Balazs Lecz
    except EnvironmentError, err:
1861 b73360e3 Balazs Lecz
      raise errors.GenericError("Cannot change directory permissions on"
1862 b73360e3 Balazs Lecz
                                " '%s': %s" % (dir_name, err))
1863 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1864 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1865 9dae41ad Guido Trotter
1866 9dae41ad Guido Trotter
1867 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1868 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1869 ca0aa6d0 Michael Hanselmann

1870 016308cb Iustin Pop
  @type size: int
1871 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1872 58885d79 Iustin Pop
  @rtype: str
1873 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1874 ca0aa6d0 Michael Hanselmann

1875 ca0aa6d0 Michael Hanselmann
  """
1876 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1877 ca0aa6d0 Michael Hanselmann
  try:
1878 582ed043 Guido Trotter
    return f.read(size)
1879 ca0aa6d0 Michael Hanselmann
  finally:
1880 ca0aa6d0 Michael Hanselmann
    f.close()
1881 ca0aa6d0 Michael Hanselmann
1882 ca0aa6d0 Michael Hanselmann
1883 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1884 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1885 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1886 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1887 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1888 087b34fe Iustin Pop
  """(Over)write a file atomically.
1889 087b34fe Iustin Pop

1890 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1891 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1892 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1893 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1894 087b34fe Iustin Pop
  mtime/atime of the file.
1895 087b34fe Iustin Pop

1896 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1897 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1898 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1899 087b34fe Iustin Pop
  temporary file should be removed.
1900 087b34fe Iustin Pop

1901 58885d79 Iustin Pop
  @type file_name: str
1902 58885d79 Iustin Pop
  @param file_name: the target filename
1903 58885d79 Iustin Pop
  @type fn: callable
1904 58885d79 Iustin Pop
  @param fn: content writing function, called with
1905 58885d79 Iustin Pop
      file descriptor as parameter
1906 69efe319 Michael Hanselmann
  @type data: str
1907 58885d79 Iustin Pop
  @param data: contents of the file
1908 58885d79 Iustin Pop
  @type mode: int
1909 58885d79 Iustin Pop
  @param mode: file mode
1910 58885d79 Iustin Pop
  @type uid: int
1911 58885d79 Iustin Pop
  @param uid: the owner of the file
1912 58885d79 Iustin Pop
  @type gid: int
1913 58885d79 Iustin Pop
  @param gid: the group of the file
1914 58885d79 Iustin Pop
  @type atime: int
1915 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1916 58885d79 Iustin Pop
  @type mtime: int
1917 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1918 58885d79 Iustin Pop
  @type close: boolean
1919 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1920 58885d79 Iustin Pop
  @type prewrite: callable
1921 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1922 58885d79 Iustin Pop
  @type postwrite: callable
1923 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1924 58885d79 Iustin Pop

1925 58885d79 Iustin Pop
  @rtype: None or int
1926 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1927 58885d79 Iustin Pop
      otherwise the file descriptor
1928 58885d79 Iustin Pop

1929 69efe319 Michael Hanselmann
  @raise errors.ProgrammerError: if any of the arguments are not valid
1930 71714516 Michael Hanselmann

1931 087b34fe Iustin Pop
  """
1932 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1933 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1934 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1935 087b34fe Iustin Pop
1936 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1937 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1938 087b34fe Iustin Pop
1939 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1940 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1941 087b34fe Iustin Pop
                                 " set or None")
1942 087b34fe Iustin Pop
1943 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1944 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1945 087b34fe Iustin Pop
1946 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1947 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1948 81b7354c Iustin Pop
  do_remove = True
1949 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1950 087b34fe Iustin Pop
  # leaves it in place
1951 087b34fe Iustin Pop
  try:
1952 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1953 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1954 087b34fe Iustin Pop
    if mode:
1955 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1956 71714516 Michael Hanselmann
    if callable(prewrite):
1957 71714516 Michael Hanselmann
      prewrite(fd)
1958 087b34fe Iustin Pop
    if data is not None:
1959 087b34fe Iustin Pop
      os.write(fd, data)
1960 087b34fe Iustin Pop
    else:
1961 087b34fe Iustin Pop
      fn(fd)
1962 71714516 Michael Hanselmann
    if callable(postwrite):
1963 71714516 Michael Hanselmann
      postwrite(fd)
1964 087b34fe Iustin Pop
    os.fsync(fd)
1965 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1966 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1967 70f4497c Michael Hanselmann
    if not dry_run:
1968 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1969 81b7354c Iustin Pop
      do_remove = False
1970 087b34fe Iustin Pop
  finally:
1971 71714516 Michael Hanselmann
    if close:
1972 71714516 Michael Hanselmann
      os.close(fd)
1973 71714516 Michael Hanselmann
      result = None
1974 71714516 Michael Hanselmann
    else:
1975 71714516 Michael Hanselmann
      result = fd
1976 81b7354c Iustin Pop
    if do_remove:
1977 81b7354c Iustin Pop
      RemoveFile(new_name)
1978 78feb6fb Guido Trotter
1979 71714516 Michael Hanselmann
  return result
1980 71714516 Michael Hanselmann
1981 78feb6fb Guido Trotter
1982 9e100285 Iustin Pop
def GetFileID(path=None, fd=None):
1983 9e100285 Iustin Pop
  """Returns the file 'id', i.e. the dev/inode and mtime information.
1984 9e100285 Iustin Pop

1985 9e100285 Iustin Pop
  Either the path to the file or the fd must be given.
1986 9e100285 Iustin Pop

1987 9e100285 Iustin Pop
  @param path: the file path
1988 9e100285 Iustin Pop
  @param fd: a file descriptor
1989 9e100285 Iustin Pop
  @return: a tuple of (device number, inode number, mtime)
1990 9e100285 Iustin Pop

1991 9e100285 Iustin Pop
  """
1992 9e100285 Iustin Pop
  if [path, fd].count(None) != 1:
1993 9e100285 Iustin Pop
    raise errors.ProgrammerError("One and only one of fd/path must be given")
1994 9e100285 Iustin Pop
1995 9e100285 Iustin Pop
  if fd is None:
1996 9e100285 Iustin Pop
    st = os.stat(path)
1997 9e100285 Iustin Pop
  else:
1998 9e100285 Iustin Pop
    st = os.fstat(fd)
1999 9e100285 Iustin Pop
2000 9e100285 Iustin Pop
  return (st.st_dev, st.st_ino, st.st_mtime)
2001 9e100285 Iustin Pop
2002 9e100285 Iustin Pop
2003 9e100285 Iustin Pop
def VerifyFileID(fi_disk, fi_ours):
2004 9e100285 Iustin Pop
  """Verifies that two file IDs are matching.
2005 9e100285 Iustin Pop

2006 9e100285 Iustin Pop
  Differences in the inode/device are not accepted, but and older
2007 9e100285 Iustin Pop
  timestamp for fi_disk is accepted.
2008 9e100285 Iustin Pop

2009 9e100285 Iustin Pop
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
2010 9e100285 Iustin Pop
      file data
2011 9e100285 Iustin Pop
  @param fi_ours: tuple (dev, inode, mtime) representing the last
2012 9e100285 Iustin Pop
      written file data
2013 9e100285 Iustin Pop
  @rtype: boolean
2014 9e100285 Iustin Pop

2015 9e100285 Iustin Pop
  """
2016 9e100285 Iustin Pop
  (d1, i1, m1) = fi_disk
2017 9e100285 Iustin Pop
  (d2, i2, m2) = fi_ours
2018 9e100285 Iustin Pop
2019 9e100285 Iustin Pop
  return (d1, i1) == (d2, i2) and m1 <= m2
2020 9e100285 Iustin Pop
2021 9e100285 Iustin Pop
2022 4138d39f Iustin Pop
def SafeWriteFile(file_name, file_id, **kwargs):
2023 4138d39f Iustin Pop
  """Wraper over L{WriteFile} that locks the target file.
2024 4138d39f Iustin Pop

2025 4138d39f Iustin Pop
  By keeping the target file locked during WriteFile, we ensure that
2026 4138d39f Iustin Pop
  cooperating writers will safely serialise access to the file.
2027 4138d39f Iustin Pop

2028 4138d39f Iustin Pop
  @type file_name: str
2029 4138d39f Iustin Pop
  @param file_name: the target filename
2030 4138d39f Iustin Pop
  @type file_id: tuple
2031 4138d39f Iustin Pop
  @param file_id: a result from L{GetFileID}
2032 4138d39f Iustin Pop

2033 4138d39f Iustin Pop
  """
2034 4138d39f Iustin Pop
  fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
2035 4138d39f Iustin Pop
  try:
2036 4138d39f Iustin Pop
    LockFile(fd)
2037 4138d39f Iustin Pop
    if file_id is not None:
2038 4138d39f Iustin Pop
      disk_id = GetFileID(fd=fd)
2039 4138d39f Iustin Pop
      if not VerifyFileID(disk_id, file_id):
2040 4138d39f Iustin Pop
        raise errors.LockError("Cannot overwrite file %s, it has been modified"
2041 4138d39f Iustin Pop
                               " since last written" % file_name)
2042 4138d39f Iustin Pop
    return WriteFile(file_name, **kwargs)
2043 4138d39f Iustin Pop
  finally:
2044 4138d39f Iustin Pop
    os.close(fd)
2045 4138d39f Iustin Pop
2046 4138d39f Iustin Pop
2047 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
2048 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
2049 e587b46a Guido Trotter

2050 e587b46a Guido Trotter
  @type strict: boolean
2051 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
2052 e587b46a Guido Trotter
      non-empty line
2053 e587b46a Guido Trotter

2054 e587b46a Guido Trotter
  """
2055 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
2056 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
2057 e587b46a Guido Trotter
  if not file_lines or not full_lines:
2058 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
2059 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
2060 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
2061 e587b46a Guido Trotter
                              file_name)
2062 e587b46a Guido Trotter
  return full_lines[0]
2063 e587b46a Guido Trotter
2064 e587b46a Guido Trotter
2065 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
2066 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
2067 7b4126b7 Iustin Pop

2068 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
2069 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
2070 7b4126b7 Iustin Pop
  value, the index will be returned.
2071 7b4126b7 Iustin Pop

2072 7b4126b7 Iustin Pop
  The base argument is used to start at a different offset,
2073 58885d79 Iustin Pop
  i.e. C{[3, 4, 6]} with I{offset=3} will return 5.
2074 58885d79 Iustin Pop

2075 58885d79 Iustin Pop
  Example: C{[0, 1, 3]} will return I{2}.
2076 7b4126b7 Iustin Pop

2077 58885d79 Iustin Pop
  @type seq: sequence
2078 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
2079 58885d79 Iustin Pop
  @type base: int
2080 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
2081 58885d79 Iustin Pop
  @rtype: int
2082 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
2083 7b4126b7 Iustin Pop

2084 7b4126b7 Iustin Pop
  """
2085 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
2086 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
2087 7b4126b7 Iustin Pop
    if elem > idx + base:
2088 7b4126b7 Iustin Pop
      # idx is not used
2089 7b4126b7 Iustin Pop
      return idx + base
2090 7b4126b7 Iustin Pop
  return None
2091 7b4126b7 Iustin Pop
2092 7b4126b7 Iustin Pop
2093 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
2094 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
2095 dcd511c8 Guido Trotter

2096 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
2097 dfdc4060 Guido Trotter

2098 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
2099 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
2100 dfdc4060 Guido Trotter
  @type event: integer
2101 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
2102 dcd511c8 Guido Trotter
  @type timeout: float or None
2103 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
2104 dcd511c8 Guido Trotter
  @rtype: int or None
2105 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
2106 dcd511c8 Guido Trotter

2107 dcd511c8 Guido Trotter
  """
2108 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
2109 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
2110 dcd511c8 Guido Trotter
2111 dcd511c8 Guido Trotter
  if timeout is not None:
2112 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
2113 dcd511c8 Guido Trotter
    timeout *= 1000
2114 dcd511c8 Guido Trotter
2115 dcd511c8 Guido Trotter
  poller = select.poll()
2116 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
2117 dcd511c8 Guido Trotter
  try:
2118 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
2119 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
2120 dfdc4060 Guido Trotter
    # every so often.
2121 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
2122 dfdc4060 Guido Trotter
  except select.error, err:
2123 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
2124 dfdc4060 Guido Trotter
      raise
2125 dfdc4060 Guido Trotter
    io_events = []
2126 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
2127 dfdc4060 Guido Trotter
    return io_events[0][1]
2128 dfdc4060 Guido Trotter
  else:
2129 dfdc4060 Guido Trotter
    return None
2130 dfdc4060 Guido Trotter
2131 dfdc4060 Guido Trotter
2132 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
2133 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
2134 dfdc4060 Guido Trotter

2135 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
2136 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
2137 dfdc4060 Guido Trotter
  expired.
2138 dfdc4060 Guido Trotter

2139 dfdc4060 Guido Trotter
  """
2140 dfdc4060 Guido Trotter
2141 dfdc4060 Guido Trotter
  def __init__(self, timeout):
2142 dfdc4060 Guido Trotter
    self.timeout = timeout
2143 dfdc4060 Guido Trotter
2144 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
2145 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
2146 dfdc4060 Guido Trotter
    if result is None:
2147 dfdc4060 Guido Trotter
      raise RetryAgain()
2148 dfdc4060 Guido Trotter
    else:
2149 dfdc4060 Guido Trotter
      return result
2150 dfdc4060 Guido Trotter
2151 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
2152 dfdc4060 Guido Trotter
    self.timeout = timeout
2153 dfdc4060 Guido Trotter
2154 dfdc4060 Guido Trotter
2155 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
2156 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
2157 dfdc4060 Guido Trotter

2158 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
2159 dfdc4060 Guido Trotter

2160 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
2161 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
2162 dfdc4060 Guido Trotter
  @type event: integer
2163 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
2164 dfdc4060 Guido Trotter
  @type timeout: float or None
2165 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
2166 dfdc4060 Guido Trotter
  @rtype: int or None
2167 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
2168 dfdc4060 Guido Trotter

2169 dfdc4060 Guido Trotter
  """
2170 dfdc4060 Guido Trotter
  if timeout is not None:
2171 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
2172 1b429e2a Iustin Pop
    try:
2173 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
2174 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
2175 1b429e2a Iustin Pop
    except RetryTimeout:
2176 1b429e2a Iustin Pop
      result = None
2177 dfdc4060 Guido Trotter
  else:
2178 dfdc4060 Guido Trotter
    result = None
2179 dfdc4060 Guido Trotter
    while result is None:
2180 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
2181 dfdc4060 Guido Trotter
  return result
2182 2de64672 Iustin Pop
2183 2de64672 Iustin Pop
2184 f7414041 Michael Hanselmann
def UniqueSequence(seq):
2185 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
2186 f7414041 Michael Hanselmann

2187 f7414041 Michael Hanselmann
  Element order is preserved.
2188 58885d79 Iustin Pop

2189 58885d79 Iustin Pop
  @type seq: sequence
2190 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
2191 58885d79 Iustin Pop
  @rtype: list
2192 58885d79 Iustin Pop
  @return: list of unique elements from seq
2193 58885d79 Iustin Pop

2194 f7414041 Michael Hanselmann
  """
2195 f7414041 Michael Hanselmann
  seen = set()
2196 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
2197 1862d460 Alexander Schreiber
2198 1862d460 Alexander Schreiber
2199 9f5a3645 Michael Hanselmann
def FindDuplicates(seq):
2200 9f5a3645 Michael Hanselmann
  """Identifies duplicates in a list.
2201 9f5a3645 Michael Hanselmann

2202 9f5a3645 Michael Hanselmann
  Does not preserve element order.
2203 9f5a3645 Michael Hanselmann

2204 9f5a3645 Michael Hanselmann
  @type seq: sequence
2205 9f5a3645 Michael Hanselmann
  @param seq: Sequence with source elements
2206 9f5a3645 Michael Hanselmann
  @rtype: list
2207 9f5a3645 Michael Hanselmann
  @return: List of duplicate elements from seq
2208 9f5a3645 Michael Hanselmann

2209 9f5a3645 Michael Hanselmann
  """
2210 9f5a3645 Michael Hanselmann
  dup = set()
2211 9f5a3645 Michael Hanselmann
  seen = set()
2212 9f5a3645 Michael Hanselmann
2213 9f5a3645 Michael Hanselmann
  for item in seq:
2214 9f5a3645 Michael Hanselmann
    if item in seen:
2215 9f5a3645 Michael Hanselmann
      dup.add(item)
2216 9f5a3645 Michael Hanselmann
    else:
2217 9f5a3645 Michael Hanselmann
      seen.add(item)
2218 9f5a3645 Michael Hanselmann
2219 9f5a3645 Michael Hanselmann
  return list(dup)
2220 9f5a3645 Michael Hanselmann
2221 9f5a3645 Michael Hanselmann
2222 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
2223 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
2224 1862d460 Alexander Schreiber

2225 5bbd3f7f Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
2226 82187135 René Nussbaumer
  accepts colon separated format. Normalize it to all lower.
2227 58885d79 Iustin Pop

2228 58885d79 Iustin Pop
  @type mac: str
2229 58885d79 Iustin Pop
  @param mac: the MAC to be validated
2230 82187135 René Nussbaumer
  @rtype: str
2231 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
2232 82187135 René Nussbaumer

2233 82187135 René Nussbaumer
  @raise errors.OpPrereqError: If the MAC isn't valid
2234 58885d79 Iustin Pop

2235 1862d460 Alexander Schreiber
  """
2236 8fb00704 Iustin Pop
  if not _MAC_CHECK.match(mac):
2237 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
2238 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
2239 82187135 René Nussbaumer
2240 82187135 René Nussbaumer
  return mac.lower()
2241 06009e27 Iustin Pop
2242 06009e27 Iustin Pop
2243 06009e27 Iustin Pop
def TestDelay(duration):
2244 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
2245 06009e27 Iustin Pop

2246 58885d79 Iustin Pop
  @type duration: float
2247 58885d79 Iustin Pop
  @param duration: the sleep duration
2248 58885d79 Iustin Pop
  @rtype: boolean
2249 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
2250 58885d79 Iustin Pop

2251 06009e27 Iustin Pop
  """
2252 06009e27 Iustin Pop
  if duration < 0:
2253 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
2254 06009e27 Iustin Pop
  time.sleep(duration)
2255 38ea42a1 Iustin Pop
  return True, None
2256 8f765069 Iustin Pop
2257 8f765069 Iustin Pop
2258 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
2259 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
2260 8f765069 Iustin Pop

2261 7d88772a Iustin Pop
  @type fd: int
2262 7d88772a Iustin Pop
  @param fd: the file descriptor
2263 7d88772a Iustin Pop
  @type retries: int
2264 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
2265 7d88772a Iustin Pop
      other error than EBADF
2266 7d88772a Iustin Pop

2267 7d88772a Iustin Pop
  """
2268 7d88772a Iustin Pop
  try:
2269 7d88772a Iustin Pop
    os.close(fd)
2270 7d88772a Iustin Pop
  except OSError, err:
2271 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
2272 7d88772a Iustin Pop
      if retries > 0:
2273 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
2274 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
2275 7d88772a Iustin Pop
    # ignore this and go on
2276 7d88772a Iustin Pop
2277 7d88772a Iustin Pop
2278 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
2279 7d88772a Iustin Pop
  """Close file descriptors.
2280 7d88772a Iustin Pop

2281 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
2282 7d88772a Iustin Pop
  stdin/out/err).
2283 8f765069 Iustin Pop

2284 58885d79 Iustin Pop
  @type noclose_fds: list or None
2285 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
2286 58885d79 Iustin Pop
      that should not be closed
2287 58885d79 Iustin Pop

2288 8f765069 Iustin Pop
  """
2289 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
2290 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
2291 8f765069 Iustin Pop
    try:
2292 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
2293 8f765069 Iustin Pop
      if MAXFD < 0:
2294 8f765069 Iustin Pop
        MAXFD = 1024
2295 8f765069 Iustin Pop
    except OSError:
2296 8f765069 Iustin Pop
      MAXFD = 1024
2297 8f765069 Iustin Pop
  else:
2298 8f765069 Iustin Pop
    MAXFD = 1024
2299 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
2300 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
2301 7d88772a Iustin Pop
    maxfd = MAXFD
2302 7d88772a Iustin Pop
2303 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
2304 7d88772a Iustin Pop
  for fd in range(3, maxfd):
2305 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
2306 7d88772a Iustin Pop
      continue
2307 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2308 7d88772a Iustin Pop
2309 7d88772a Iustin Pop
2310 4c32a8bd Luca Bigliardi
def Mlockall(_ctypes=ctypes):
2311 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
2312 4b6fa0bf Luca Bigliardi

2313 4b6fa0bf Luca Bigliardi
  This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
2314 4b6fa0bf Luca Bigliardi
  see mlock(2) for more details. This function requires ctypes module.
2315 4b6fa0bf Luca Bigliardi

2316 4c32a8bd Luca Bigliardi
  @raises errors.NoCtypesError: if ctypes module is not found
2317 4c32a8bd Luca Bigliardi

2318 4b6fa0bf Luca Bigliardi
  """
2319 4c32a8bd Luca Bigliardi
  if _ctypes is None:
2320 4c32a8bd Luca Bigliardi
    raise errors.NoCtypesError()
2321 4b6fa0bf Luca Bigliardi
2322 4c32a8bd Luca Bigliardi
  libc = _ctypes.cdll.LoadLibrary("libc.so.6")
2323 4b6fa0bf Luca Bigliardi
  if libc is None:
2324 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
2325 4b6fa0bf Luca Bigliardi
    return
2326 4b6fa0bf Luca Bigliardi
2327 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
2328 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
2329 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
2330 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
2331 4b6fa0bf Luca Bigliardi
  # the actual error code was.
2332 20601361 Luca Bigliardi
  # pylint: disable-msg=W0212
2333 4c32a8bd Luca Bigliardi
  libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
2334 4b6fa0bf Luca Bigliardi
2335 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
2336 20601361 Luca Bigliardi
    # pylint: disable-msg=W0212
2337 6ed0bbce Luca Bigliardi
    logging.error("Cannot set memory lock: %s",
2338 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
2339 4b6fa0bf Luca Bigliardi
    return
2340 4b6fa0bf Luca Bigliardi
2341 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
2342 4b6fa0bf Luca Bigliardi
2343 4b6fa0bf Luca Bigliardi
2344 0070a462 René Nussbaumer
def Daemonize(logfile):
2345 7d88772a Iustin Pop
  """Daemonize the current process.
2346 7d88772a Iustin Pop

2347 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
2348 7d88772a Iustin Pop
  runs it in the background as a daemon.
2349 7d88772a Iustin Pop

2350 7d88772a Iustin Pop
  @type logfile: str
2351 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2352 7d88772a Iustin Pop
  @rtype: int
2353 5fcc718f Iustin Pop
  @return: the value zero
2354 7d88772a Iustin Pop

2355 7d88772a Iustin Pop
  """
2356 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
2357 7260cfbe Iustin Pop
  # yes, we really want os._exit
2358 8f765069 Iustin Pop
2359 b78aa8c2 Iustin Pop
  # TODO: do another attempt to merge Daemonize and StartDaemon, or at
2360 b78aa8c2 Iustin Pop
  # least abstract the pipe functionality between them
2361 b78aa8c2 Iustin Pop
2362 b78aa8c2 Iustin Pop
  # Create pipe for sending error messages
2363 b78aa8c2 Iustin Pop
  (rpipe, wpipe) = os.pipe()
2364 b78aa8c2 Iustin Pop
2365 8f765069 Iustin Pop
  # this might fail
2366 8f765069 Iustin Pop
  pid = os.fork()
2367 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
2368 0260032c Iustin Pop
    SetupDaemonEnv()
2369 0260032c Iustin Pop
2370 8f765069 Iustin Pop
    # this might fail
2371 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
2372 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
2373 b78aa8c2 Iustin Pop
      _CloseFDNoErr(rpipe)
2374 8f765069 Iustin Pop
    else:
2375 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
2376 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
2377 8f765069 Iustin Pop
  else:
2378 b78aa8c2 Iustin Pop
    _CloseFDNoErr(wpipe)
2379 b78aa8c2 Iustin Pop
    # Wait for daemon to be started (or an error message to
2380 b78aa8c2 Iustin Pop
    # arrive) and read up to 100 KB as an error message
2381 b78aa8c2 Iustin Pop
    errormsg = RetryOnSignal(os.read, rpipe, 100 * 1024)
2382 b78aa8c2 Iustin Pop
    if errormsg:
2383 b78aa8c2 Iustin Pop
      sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
2384 b78aa8c2 Iustin Pop
      rcode = 1
2385 b78aa8c2 Iustin Pop
    else:
2386 b78aa8c2 Iustin Pop
      rcode = 0
2387 b78aa8c2 Iustin Pop
    os._exit(rcode) # Exit parent of the first child.
2388 8f765069 Iustin Pop
2389 79634555 Iustin Pop
  SetupDaemonFDs(logfile, None)
2390 b78aa8c2 Iustin Pop
  return wpipe
2391 57c177af Iustin Pop
2392 57c177af Iustin Pop
2393 53beffbb Iustin Pop
def DaemonPidFileName(name):
2394 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
2395 58885d79 Iustin Pop

2396 58885d79 Iustin Pop
  @type name: str
2397 58885d79 Iustin Pop
  @param name: the daemon name
2398 58885d79 Iustin Pop
  @rtype: str
2399 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2400 58885d79 Iustin Pop
      daemon name
2401 b330ac0b Guido Trotter

2402 b330ac0b Guido Trotter
  """
2403 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2404 b330ac0b Guido Trotter
2405 b330ac0b Guido Trotter
2406 2826b361 Guido Trotter
def EnsureDaemon(name):
2407 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2408 2826b361 Guido Trotter

2409 2826b361 Guido Trotter
  """
2410 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2411 2826b361 Guido Trotter
  if result.failed:
2412 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2413 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2414 2826b361 Guido Trotter
    return False
2415 2826b361 Guido Trotter
2416 2826b361 Guido Trotter
  return True
2417 b330ac0b Guido Trotter
2418 b330ac0b Guido Trotter
2419 db147305 Tom Limoncelli
def StopDaemon(name):
2420 db147305 Tom Limoncelli
  """Stop daemon
2421 db147305 Tom Limoncelli

2422 db147305 Tom Limoncelli
  """
2423 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
2424 db147305 Tom Limoncelli
  if result.failed:
2425 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
2426 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
2427 db147305 Tom Limoncelli
    return False
2428 db147305 Tom Limoncelli
2429 db147305 Tom Limoncelli
  return True
2430 db147305 Tom Limoncelli
2431 db147305 Tom Limoncelli
2432 5c4d37f9 Iustin Pop
def WritePidFile(pidfile):
2433 b330ac0b Guido Trotter
  """Write the current process pidfile.
2434 b330ac0b Guido Trotter

2435 614244bd Iustin Pop
  @type pidfile: sting
2436 614244bd Iustin Pop
  @param pidfile: the path to the file to be written
2437 614244bd Iustin Pop
  @raise errors.LockError: if the pid file already exists and
2438 58885d79 Iustin Pop
      points to a live process
2439 614244bd Iustin Pop
  @rtype: int
2440 614244bd Iustin Pop
  @return: the file descriptor of the lock file; do not close this unless
2441 614244bd Iustin Pop
      you want to unlock the pid file
2442 b330ac0b Guido Trotter

2443 b330ac0b Guido Trotter
  """
2444 5c4d37f9 Iustin Pop
  # We don't rename nor truncate the file to not drop locks under
2445 5c4d37f9 Iustin Pop
  # existing processes
2446 5c4d37f9 Iustin Pop
  fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
2447 5c4d37f9 Iustin Pop
2448 5c4d37f9 Iustin Pop
  # Lock the PID file (and fail if not possible to do so). Any code
2449 5c4d37f9 Iustin Pop
  # wanting to send a signal to the daemon should try to lock the PID
2450 5c4d37f9 Iustin Pop
  # file before reading it. If acquiring the lock succeeds, the daemon is
2451 5c4d37f9 Iustin Pop
  # no longer running and the signal should not be sent.
2452 5c4d37f9 Iustin Pop
  LockFile(fd_pidfile)
2453 5c4d37f9 Iustin Pop
2454 5c4d37f9 Iustin Pop
  os.write(fd_pidfile, "%d\n" % os.getpid())
2455 b330ac0b Guido Trotter
2456 5c4d37f9 Iustin Pop
  return fd_pidfile
2457 b330ac0b Guido Trotter
2458 b330ac0b Guido Trotter
2459 b330ac0b Guido Trotter
def RemovePidFile(name):
2460 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2461 b330ac0b Guido Trotter

2462 b330ac0b Guido Trotter
  Any errors are ignored.
2463 b330ac0b Guido Trotter

2464 58885d79 Iustin Pop
  @type name: str
2465 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2466 58885d79 Iustin Pop

2467 b330ac0b Guido Trotter
  """
2468 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2469 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2470 b330ac0b Guido Trotter
  try:
2471 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2472 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2473 b330ac0b Guido Trotter
    pass
2474 b330ac0b Guido Trotter
2475 b330ac0b Guido Trotter
2476 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2477 ff5251bc Iustin Pop
                waitpid=False):
2478 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2479 b2a1f511 Iustin Pop

2480 b2a1f511 Iustin Pop
  @type pid: int
2481 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
2482 38206f3c Iustin Pop
  @type signal_: int
2483 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
2484 b2a1f511 Iustin Pop
  @type timeout: int
2485 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
2486 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
2487 b2a1f511 Iustin Pop
                  will be done
2488 ff5251bc Iustin Pop
  @type waitpid: boolean
2489 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
2490 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
2491 ff5251bc Iustin Pop
      would remain as zombie
2492 b2a1f511 Iustin Pop

2493 b2a1f511 Iustin Pop
  """
2494 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
2495 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
2496 560cbec1 Michael Hanselmann
    if IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
2497 ff5251bc Iustin Pop
      try:
2498 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
2499 ff5251bc Iustin Pop
      except OSError:
2500 ff5251bc Iustin Pop
        pass
2501 ff5251bc Iustin Pop
2502 b2a1f511 Iustin Pop
  if pid <= 0:
2503 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
2504 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
2505 b2a1f511 Iustin Pop
2506 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
2507 b2a1f511 Iustin Pop
    return
2508 31892b4c Michael Hanselmann
2509 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
2510 31892b4c Michael Hanselmann
2511 b2a1f511 Iustin Pop
  if timeout <= 0:
2512 b2a1f511 Iustin Pop
    return
2513 7167159a Michael Hanselmann
2514 31892b4c Michael Hanselmann
  def _CheckProcess():
2515 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
2516 31892b4c Michael Hanselmann
      return
2517 31892b4c Michael Hanselmann
2518 7167159a Michael Hanselmann
    try:
2519 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
2520 7167159a Michael Hanselmann
    except OSError:
2521 31892b4c Michael Hanselmann
      raise RetryAgain()
2522 31892b4c Michael Hanselmann
2523 31892b4c Michael Hanselmann
    if result_pid > 0:
2524 31892b4c Michael Hanselmann
      return
2525 31892b4c Michael Hanselmann
2526 31892b4c Michael Hanselmann
    raise RetryAgain()
2527 31892b4c Michael Hanselmann
2528 31892b4c Michael Hanselmann
  try:
2529 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
2530 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
2531 31892b4c Michael Hanselmann
  except RetryTimeout:
2532 31892b4c Michael Hanselmann
    pass
2533 7167159a Michael Hanselmann
2534 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
2535 7167159a Michael Hanselmann
    # Kill process if it's still alive
2536 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
2537 b2a1f511 Iustin Pop
2538 b2a1f511 Iustin Pop
2539 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
2540 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
2541 57c177af Iustin Pop

2542 57c177af Iustin Pop
  This is an abstract method to search for filesystem object (files,
2543 57c177af Iustin Pop
  dirs) under a given search path.
2544 57c177af Iustin Pop

2545 58885d79 Iustin Pop
  @type name: str
2546 58885d79 Iustin Pop
  @param name: the name to look for
2547 58885d79 Iustin Pop
  @type search_path: str
2548 58885d79 Iustin Pop
  @param search_path: location to start at
2549 58885d79 Iustin Pop
  @type test: callable
2550 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2551 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2552 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2553 58885d79 Iustin Pop
  @rtype: str or None
2554 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2555 57c177af Iustin Pop

2556 57c177af Iustin Pop
  """
2557 f95c81bf Iustin Pop
  # validate the filename mask
2558 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
2559 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
2560 f95c81bf Iustin Pop
                     name)
2561 f95c81bf Iustin Pop
    return None
2562 f95c81bf Iustin Pop
2563 57c177af Iustin Pop
  for dir_name in search_path:
2564 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
2565 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
2566 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
2567 f95c81bf Iustin Pop
    # basename
2568 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
2569 57c177af Iustin Pop
      return item_name
2570 57c177af Iustin Pop
  return None
2571 8d1a2a64 Michael Hanselmann
2572 8d1a2a64 Michael Hanselmann
2573 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
2574 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
2575 8d1a2a64 Michael Hanselmann

2576 58885d79 Iustin Pop
  The function will check if a given volume group is in the list of
2577 58885d79 Iustin Pop
  volume groups and has a minimum size.
2578 58885d79 Iustin Pop

2579 58885d79 Iustin Pop
  @type vglist: dict
2580 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2581 58885d79 Iustin Pop
  @type vgname: str
2582 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2583 58885d79 Iustin Pop
  @type minsize: int
2584 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2585 58885d79 Iustin Pop
  @rtype: None or str
2586 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2587 8d1a2a64 Michael Hanselmann

2588 8d1a2a64 Michael Hanselmann
  """
2589 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2590 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2591 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2592 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2593 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2594 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2595 8d1a2a64 Michael Hanselmann
  return None
2596 7996a135 Iustin Pop
2597 7996a135 Iustin Pop
2598 45bc5e4a Michael Hanselmann
def SplitTime(value):
2599 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2600 739be818 Michael Hanselmann

2601 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2602 45bc5e4a Michael Hanselmann
  @type value: int or float
2603 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2604 739be818 Michael Hanselmann

2605 739be818 Michael Hanselmann
  """
2606 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2607 45bc5e4a Michael Hanselmann
2608 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2609 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2610 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2611 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2612 45bc5e4a Michael Hanselmann
2613 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2614 739be818 Michael Hanselmann
2615 739be818 Michael Hanselmann
2616 739be818 Michael Hanselmann
def MergeTime(timetuple):
2617 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2618 739be818 Michael Hanselmann

2619 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2620 739be818 Michael Hanselmann
  @type timetuple: tuple
2621 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2622 739be818 Michael Hanselmann

2623 739be818 Michael Hanselmann
  """
2624 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2625 739be818 Michael Hanselmann
2626 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2627 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2628 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2629 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2630 739be818 Michael Hanselmann
2631 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2632 739be818 Michael Hanselmann
2633 739be818 Michael Hanselmann
2634 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2635 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2636 de3b8e39 Luca Bigliardi

2637 de3b8e39 Luca Bigliardi
  When an error occurs while writing on the logfile, logging.FileHandler tries
2638 de3b8e39 Luca Bigliardi
  to log on stderr. This doesn't work in ganeti since stderr is redirected to
2639 de3b8e39 Luca Bigliardi
  the logfile. This class avoids failures reporting errors to /dev/console.
2640 de3b8e39 Luca Bigliardi

2641 de3b8e39 Luca Bigliardi
  """
2642 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2643 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2644 de3b8e39 Luca Bigliardi

2645 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2646 de3b8e39 Luca Bigliardi

2647 de3b8e39 Luca Bigliardi
    """
2648 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2649 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2650 de3b8e39 Luca Bigliardi
2651 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2652 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2653 de3b8e39 Luca Bigliardi

2654 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2655 de3b8e39 Luca Bigliardi
    /dev/console.
2656 de3b8e39 Luca Bigliardi

2657 de3b8e39 Luca Bigliardi
    """
2658 de3b8e39 Luca Bigliardi
    try:
2659 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2660 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2661 de3b8e39 Luca Bigliardi
      try:
2662 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2663 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2664 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2665 de3b8e39 Luca Bigliardi
        pass
2666 de3b8e39 Luca Bigliardi
2667 de3b8e39 Luca Bigliardi
2668 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2669 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2670 49e60a28 Luca Bigliardi
                 console_logging=False):
2671 82d9caef Iustin Pop
  """Configures the logging module.
2672 82d9caef Iustin Pop

2673 58885d79 Iustin Pop
  @type logfile: str
2674 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2675 ea34193f Iustin Pop
  @type debug: integer
2676 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2677 58885d79 Iustin Pop
      only those at C{INFO} and above level
2678 58885d79 Iustin Pop
  @type stderr_logging: boolean
2679 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2680 58885d79 Iustin Pop
  @type program: str
2681 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2682 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2683 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2684 551b6283 Iustin Pop
  @type syslog: string
2685 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2686 551b6283 Iustin Pop
      - if no, syslog is not used
2687 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2688 551b6283 Iustin Pop
      - if only, only syslog is used
2689 49e60a28 Luca Bigliardi
  @type console_logging: boolean
2690 49e60a28 Luca Bigliardi
  @param console_logging: if True, will use a FileHandler which falls back to
2691 49e60a28 Luca Bigliardi
      the system console if logging fails
2692 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2693 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2694 58885d79 Iustin Pop

2695 82d9caef Iustin Pop
  """
2696 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2697 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2698 d21d09d6 Iustin Pop
  if multithreaded:
2699 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2700 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2701 82d9caef Iustin Pop
  if debug:
2702 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2703 551b6283 Iustin Pop
    # no debug info for syslog loggers
2704 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2705 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2706 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2707 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2708 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2709 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2710 82d9caef Iustin Pop
2711 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2712 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2713 82d9caef Iustin Pop
2714 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2715 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2716 7d88772a Iustin Pop
    handler.close()
2717 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2718 6346a9e5 Michael Hanselmann
2719 82d9caef Iustin Pop
  if stderr_logging:
2720 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2721 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2722 82d9caef Iustin Pop
    if debug:
2723 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2724 82d9caef Iustin Pop
    else:
2725 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2726 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2727 82d9caef Iustin Pop
2728 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2729 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2730 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2731 551b6283 Iustin Pop
                                                    facility)
2732 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2733 551b6283 Iustin Pop
    # Never enable debug over syslog
2734 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2735 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2736 551b6283 Iustin Pop
2737 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2738 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2739 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2740 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2741 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2742 551b6283 Iustin Pop
    try:
2743 49e60a28 Luca Bigliardi
      if console_logging:
2744 49e60a28 Luca Bigliardi
        logfile_handler = LogFileHandler(logfile)
2745 49e60a28 Luca Bigliardi
      else:
2746 49e60a28 Luca Bigliardi
        logfile_handler = logging.FileHandler(logfile)
2747 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2748 551b6283 Iustin Pop
      if debug:
2749 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2750 551b6283 Iustin Pop
      else:
2751 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2752 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2753 551b6283 Iustin Pop
    except EnvironmentError:
2754 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2755 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2756 551b6283 Iustin Pop
      else:
2757 551b6283 Iustin Pop
        # we need to re-raise the exception
2758 551b6283 Iustin Pop
        raise
2759 82d9caef Iustin Pop
2760 016d04b3 Michael Hanselmann
2761 da961187 Guido Trotter
def IsNormAbsPath(path):
2762 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2763 da961187 Guido Trotter

2764 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2765 da961187 Guido Trotter

2766 da961187 Guido Trotter
  """
2767 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2768 82d9caef Iustin Pop
2769 016d04b3 Michael Hanselmann
2770 4bb678e9 Iustin Pop
def PathJoin(*args):
2771 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2772 4bb678e9 Iustin Pop

2773 4bb678e9 Iustin Pop
  Requirements:
2774 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2775 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2776 4bb678e9 Iustin Pop
        since we check for normalization at the end
2777 4bb678e9 Iustin Pop

2778 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2779 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2780 4bb678e9 Iustin Pop

2781 4bb678e9 Iustin Pop
  """
2782 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2783 4bb678e9 Iustin Pop
  assert args
2784 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2785 4bb678e9 Iustin Pop
  root = args[0]
2786 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2787 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2788 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2789 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2790 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2791 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2792 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2793 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2794 4bb678e9 Iustin Pop
  if prefix != root:
2795 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2796 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2797 4bb678e9 Iustin Pop
  return result
2798 4bb678e9 Iustin Pop
2799 4bb678e9 Iustin Pop
2800 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2801 f65f63ef Iustin Pop
  """Return the last lines from a file.
2802 f65f63ef Iustin Pop

2803 f65f63ef Iustin Pop
  @note: this function will only read and parse the last 4KB of
2804 f65f63ef Iustin Pop
      the file; if the lines are very long, it could be that less
2805 f65f63ef Iustin Pop
      than the requested number of lines are returned
2806 f65f63ef Iustin Pop

2807 f65f63ef Iustin Pop
  @param fname: the file name
2808 f65f63ef Iustin Pop
  @type lines: int
2809 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2810 f65f63ef Iustin Pop

2811 f65f63ef Iustin Pop
  """
2812 f65f63ef Iustin Pop
  fd = open(fname, "r")
2813 f65f63ef Iustin Pop
  try:
2814 f65f63ef Iustin Pop
    fd.seek(0, 2)
2815 f65f63ef Iustin Pop
    pos = fd.tell()
2816 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2817 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2818 f65f63ef Iustin Pop
    raw_data = fd.read()
2819 f65f63ef Iustin Pop
  finally:
2820 f65f63ef Iustin Pop
    fd.close()
2821 f65f63ef Iustin Pop
2822 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2823 f65f63ef Iustin Pop
  return rows[-lines:]
2824 f65f63ef Iustin Pop
2825 f65f63ef Iustin Pop
2826 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2827 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2828 24d70417 Michael Hanselmann

2829 24d70417 Michael Hanselmann
  """
2830 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2831 24d70417 Michael Hanselmann
2832 24d70417 Michael Hanselmann
2833 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2834 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2835 27e46076 Michael Hanselmann

2836 27e46076 Michael Hanselmann
  @type value: string
2837 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2838 27e46076 Michael Hanselmann

2839 27e46076 Michael Hanselmann
  """
2840 f394c0de Iustin Pop
  m = _ASN1_TIME_REGEX.match(value)
2841 27e46076 Michael Hanselmann
  if m:
2842 27e46076 Michael Hanselmann
    # We have an offset
2843 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2844 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2845 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2846 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2847 27e46076 Michael Hanselmann
  else:
2848 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2849 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2850 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2851 27e46076 Michael Hanselmann
    utcoffset = 0
2852 27e46076 Michael Hanselmann
2853 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2854 27e46076 Michael Hanselmann
2855 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2856 27e46076 Michael Hanselmann
2857 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2858 27e46076 Michael Hanselmann
2859 27e46076 Michael Hanselmann
2860 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2861 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2862 27e46076 Michael Hanselmann

2863 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2864 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2865 27e46076 Michael Hanselmann

2866 27e46076 Michael Hanselmann
  """
2867 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2868 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2869 27e46076 Michael Hanselmann
  try:
2870 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2871 27e46076 Michael Hanselmann
  except AttributeError:
2872 27e46076 Michael Hanselmann
    not_before = None
2873 27e46076 Michael Hanselmann
  else:
2874 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2875 27e46076 Michael Hanselmann
2876 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2877 27e46076 Michael Hanselmann
      not_before = None
2878 27e46076 Michael Hanselmann
    else:
2879 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2880 27e46076 Michael Hanselmann
2881 27e46076 Michael Hanselmann
  try:
2882 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2883 27e46076 Michael Hanselmann
  except AttributeError:
2884 27e46076 Michael Hanselmann
    not_after = None
2885 27e46076 Michael Hanselmann
  else:
2886 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2887 27e46076 Michael Hanselmann
2888 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2889 27e46076 Michael Hanselmann
      not_after = None
2890 27e46076 Michael Hanselmann
    else:
2891 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2892 27e46076 Michael Hanselmann
2893 27e46076 Michael Hanselmann
  return (not_before, not_after)
2894 27e46076 Michael Hanselmann
2895 27e46076 Michael Hanselmann
2896 24d70417 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
2897 24d70417 Michael Hanselmann
                            warn_days, error_days):
2898 24d70417 Michael Hanselmann
  """Verifies certificate validity.
2899 24d70417 Michael Hanselmann

2900 24d70417 Michael Hanselmann
  @type expired: bool
2901 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2902 24d70417 Michael Hanselmann
  @type not_before: number or None
2903 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2904 24d70417 Michael Hanselmann
  @type not_after: number or None
2905 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2906 24d70417 Michael Hanselmann
  @type now: number
2907 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2908 24d70417 Michael Hanselmann
  @type warn_days: number or None
2909 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2910 24d70417 Michael Hanselmann
  @type error_days: number or None
2911 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2912 24d70417 Michael Hanselmann

2913 24d70417 Michael Hanselmann
  """
2914 24d70417 Michael Hanselmann
  if expired:
2915 24d70417 Michael Hanselmann
    msg = "Certificate is expired"
2916 24d70417 Michael Hanselmann
2917 24d70417 Michael Hanselmann
    if not_before is not None and not_after is not None:
2918 24d70417 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
2919 24d70417 Michael Hanselmann
              (FormatTimestampWithTZ(not_before),
2920 24d70417 Michael Hanselmann
               FormatTimestampWithTZ(not_after)))
2921 24d70417 Michael Hanselmann
    elif not_before is not None:
2922 24d70417 Michael Hanselmann
      msg += " (valid from %s)" % FormatTimestampWithTZ(not_before)
2923 24d70417 Michael Hanselmann
    elif not_after is not None:
2924 24d70417 Michael Hanselmann
      msg += " (valid until %s)" % FormatTimestampWithTZ(not_after)
2925 24d70417 Michael Hanselmann
2926 24d70417 Michael Hanselmann
    return (CERT_ERROR, msg)
2927 24d70417 Michael Hanselmann
2928 24d70417 Michael Hanselmann
  elif not_before is not None and not_before > now:
2929 24d70417 Michael Hanselmann
    return (CERT_WARNING,
2930 24d70417 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
2931 24d70417 Michael Hanselmann
            FormatTimestampWithTZ(not_before))
2932 24d70417 Michael Hanselmann
2933 24d70417 Michael Hanselmann
  elif not_after is not None:
2934 24d70417 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
2935 24d70417 Michael Hanselmann
2936 24d70417 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
2937 24d70417 Michael Hanselmann
2938 24d70417 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
2939 24d70417 Michael Hanselmann
      return (CERT_ERROR, msg)
2940 24d70417 Michael Hanselmann
2941 24d70417 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
2942 24d70417 Michael Hanselmann
      return (CERT_WARNING, msg)
2943 24d70417 Michael Hanselmann
2944 24d70417 Michael Hanselmann
  return (None, None)
2945 24d70417 Michael Hanselmann
2946 24d70417 Michael Hanselmann
2947 24d70417 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
2948 24d70417 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
2949 24d70417 Michael Hanselmann

2950 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2951 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2952 24d70417 Michael Hanselmann
  @type warn_days: number or None
2953 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2954 24d70417 Michael Hanselmann
  @type error_days: number or None
2955 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2956 24d70417 Michael Hanselmann

2957 24d70417 Michael Hanselmann
  """
2958 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2959 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2960 24d70417 Michael Hanselmann
2961 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2962 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2963 24d70417 Michael Hanselmann
2964 24d70417 Michael Hanselmann
2965 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2966 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2967 68857643 Michael Hanselmann

2968 68857643 Michael Hanselmann
  An RFC822-like signature header is added in front of the certificate.
2969 68857643 Michael Hanselmann

2970 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2971 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2972 68857643 Michael Hanselmann
  @type key: string
2973 68857643 Michael Hanselmann
  @param key: Key for HMAC
2974 68857643 Michael Hanselmann
  @type salt: string
2975 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2976 68857643 Michael Hanselmann
  @rtype: string
2977 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2978 68857643 Michael Hanselmann

2979 68857643 Michael Hanselmann
  """
2980 68857643 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2981 68857643 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
2982 68857643 Michael Hanselmann
2983 68857643 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
2984 68857643 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2985 68857643 Michael Hanselmann
2986 68857643 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
2987 68857643 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
2988 3718bf6d Michael Hanselmann
           Sha1Hmac(key, cert_pem, salt=salt),
2989 68857643 Michael Hanselmann
           cert_pem))
2990 68857643 Michael Hanselmann
2991 68857643 Michael Hanselmann
2992 68857643 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
2993 68857643 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
2994 68857643 Michael Hanselmann

2995 68857643 Michael Hanselmann
  """
2996 68857643 Michael Hanselmann
  # Extract signature from original PEM data
2997 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
2998 68857643 Michael Hanselmann
    if line.startswith("---"):
2999 68857643 Michael Hanselmann
      break
3000 68857643 Michael Hanselmann
3001 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
3002 68857643 Michael Hanselmann
    if m:
3003 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
3004 68857643 Michael Hanselmann
3005 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
3006 68857643 Michael Hanselmann
3007 68857643 Michael Hanselmann
3008 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
3009 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
3010 68857643 Michael Hanselmann

3011 68857643 Michael Hanselmann
  @type cert_pem: string
3012 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
3013 68857643 Michael Hanselmann
  @type key: string
3014 68857643 Michael Hanselmann
  @param key: Key for HMAC
3015 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
3016 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
3017 68857643 Michael Hanselmann

3018 68857643 Michael Hanselmann
  """
3019 68857643 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
3020 68857643 Michael Hanselmann
3021 68857643 Michael Hanselmann
  # Load certificate
3022 68857643 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
3023 68857643 Michael Hanselmann
3024 68857643 Michael Hanselmann
  # Dump again to ensure it's in a sane format
3025 68857643 Michael Hanselmann
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3026 68857643 Michael Hanselmann
3027 3718bf6d Michael Hanselmann
  if not VerifySha1Hmac(key, sane_pem, signature, salt=salt):
3028 68857643 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
3029 68857643 Michael Hanselmann
3030 68857643 Michael Hanselmann
  return (cert, salt)
3031 68857643 Michael Hanselmann
3032 68857643 Michael Hanselmann
3033 3718bf6d Michael Hanselmann
def Sha1Hmac(key, text, salt=None):
3034 615aaaba Michael Hanselmann
  """Calculates the HMAC-SHA1 digest of a text.
3035 615aaaba Michael Hanselmann

3036 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
3037 615aaaba Michael Hanselmann

3038 615aaaba Michael Hanselmann
  @type key: string
3039 615aaaba Michael Hanselmann
  @param key: Secret key
3040 615aaaba Michael Hanselmann
  @type text: string
3041 615aaaba Michael Hanselmann

3042 615aaaba Michael Hanselmann
  """
3043 3718bf6d Michael Hanselmann
  if salt:
3044 3718bf6d Michael Hanselmann
    salted_text = salt + text
3045 3718bf6d Michael Hanselmann
  else:
3046 3718bf6d Michael Hanselmann
    salted_text = text
3047 3718bf6d Michael Hanselmann
3048 716a32cb Guido Trotter
  return hmac.new(key, salted_text, compat.sha1).hexdigest()
3049 615aaaba Michael Hanselmann
3050 615aaaba Michael Hanselmann
3051 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
3052 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
3053 615aaaba Michael Hanselmann

3054 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
3055 615aaaba Michael Hanselmann

3056 615aaaba Michael Hanselmann
  @type key: string
3057 615aaaba Michael Hanselmann
  @param key: Secret key
3058 615aaaba Michael Hanselmann
  @type text: string
3059 615aaaba Michael Hanselmann
  @type digest: string
3060 615aaaba Michael Hanselmann
  @param digest: Expected digest
3061 615aaaba Michael Hanselmann
  @rtype: bool
3062 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
3063 615aaaba Michael Hanselmann

3064 615aaaba Michael Hanselmann
  """
3065 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
3066 615aaaba Michael Hanselmann
3067 615aaaba Michael Hanselmann
3068 26f15862 Iustin Pop
def SafeEncode(text):
3069 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
3070 26f15862 Iustin Pop

3071 26f15862 Iustin Pop
  This function mangles the input string and returns a version that
3072 d392fa34 Iustin Pop
  should be safe to display/encode as ASCII. To this end, we first
3073 26f15862 Iustin Pop
  convert it to ASCII using the 'backslashreplace' encoding which
3074 d392fa34 Iustin Pop
  should get rid of any non-ASCII chars, and then we process it
3075 d392fa34 Iustin Pop
  through a loop copied from the string repr sources in the python; we
3076 d392fa34 Iustin Pop
  don't use string_escape anymore since that escape single quotes and
3077 d392fa34 Iustin Pop
  backslashes too, and that is too much; and that escaping is not
3078 d392fa34 Iustin Pop
  stable, i.e. string_escape(string_escape(x)) != string_escape(x).
3079 26f15862 Iustin Pop

3080 26f15862 Iustin Pop
  @type text: str or unicode
3081 26f15862 Iustin Pop
  @param text: input data
3082 26f15862 Iustin Pop
  @rtype: str
3083 26f15862 Iustin Pop
  @return: a safe version of text
3084 26f15862 Iustin Pop

3085 26f15862 Iustin Pop
  """
3086 d392fa34 Iustin Pop
  if isinstance(text, unicode):
3087 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
3088 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
3089 d392fa34 Iustin Pop
  resu = ""
3090 d392fa34 Iustin Pop
  for char in text:
3091 d392fa34 Iustin Pop
    c = ord(char)
3092 d392fa34 Iustin Pop
    if char  == '\t':
3093 d392fa34 Iustin Pop
      resu += r'\t'
3094 d392fa34 Iustin Pop
    elif char == '\n':
3095 d392fa34 Iustin Pop
      resu += r'\n'
3096 d392fa34 Iustin Pop
    elif char == '\r':
3097 d392fa34 Iustin Pop
      resu += r'\'r'
3098 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
3099 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
3100 d392fa34 Iustin Pop
    else:
3101 d392fa34 Iustin Pop
      resu += char
3102 d392fa34 Iustin Pop
  return resu
3103 26f15862 Iustin Pop
3104 26f15862 Iustin Pop
3105 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
3106 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
3107 5b69bc7c Iustin Pop

3108 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
3109 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
3110 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
3111 5b69bc7c Iustin Pop
  separator):
3112 5b69bc7c Iustin Pop
    - a plain , separates the elements
3113 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
3114 5b69bc7c Iustin Pop
      backslash plus a separator comma
3115 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
3116 5b69bc7c Iustin Pop
      non-separator comma
3117 5b69bc7c Iustin Pop

3118 5b69bc7c Iustin Pop
  @type text: string
3119 5b69bc7c Iustin Pop
  @param text: the string to split
3120 5b69bc7c Iustin Pop
  @type sep: string
3121 5b69bc7c Iustin Pop
  @param text: the separator
3122 5b69bc7c Iustin Pop
  @rtype: string
3123 5b69bc7c Iustin Pop
  @return: a list of strings
3124 5b69bc7c Iustin Pop

3125 5b69bc7c Iustin Pop
  """
3126 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
3127 5b69bc7c Iustin Pop
  slist = text.split(sep)
3128 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
3129 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
3130 5b69bc7c Iustin Pop
  rlist = []
3131 5b69bc7c Iustin Pop
  while slist:
3132 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
3133 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
3134 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
3135 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
3136 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
3137 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
3138 5b69bc7c Iustin Pop
        # the next step
3139 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
3140 5b69bc7c Iustin Pop
        continue
3141 5b69bc7c Iustin Pop
    rlist.append(e1)
3142 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
3143 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
3144 5b69bc7c Iustin Pop
  return rlist
3145 5b69bc7c Iustin Pop
3146 5b69bc7c Iustin Pop
3147 ab3e6da8 Iustin Pop
def CommaJoin(names):
3148 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
3149 ab3e6da8 Iustin Pop

3150 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
3151 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
3152 ab3e6da8 Iustin Pop

3153 ab3e6da8 Iustin Pop
  """
3154 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
3155 ab3e6da8 Iustin Pop
3156 ab3e6da8 Iustin Pop
3157 691c81b7 Michael Hanselmann
def FindMatch(data, name):
3158 691c81b7 Michael Hanselmann
  """Tries to find an item in a dictionary matching a name.
3159 691c81b7 Michael Hanselmann

3160 691c81b7 Michael Hanselmann
  Callers have to ensure the data names aren't contradictory (e.g. a regexp
3161 691c81b7 Michael Hanselmann
  that matches a string). If the name isn't a direct key, all regular
3162 691c81b7 Michael Hanselmann
  expression objects in the dictionary are matched against it.
3163 691c81b7 Michael Hanselmann

3164 691c81b7 Michael Hanselmann
  @type data: dict
3165 691c81b7 Michael Hanselmann
  @param data: Dictionary containing data
3166 691c81b7 Michael Hanselmann
  @type name: string
3167 691c81b7 Michael Hanselmann
  @param name: Name to look for
3168 691c81b7 Michael Hanselmann
  @rtype: tuple; (value in dictionary, matched groups as list)
3169 691c81b7 Michael Hanselmann

3170 691c81b7 Michael Hanselmann
  """
3171 691c81b7 Michael Hanselmann
  if name in data:
3172 691c81b7 Michael Hanselmann
    return (data[name], [])
3173 691c81b7 Michael Hanselmann
3174 691c81b7 Michael Hanselmann
  for key, value in data.items():
3175 691c81b7 Michael Hanselmann
    # Regex objects
3176 691c81b7 Michael Hanselmann
    if hasattr(key, "match"):
3177 691c81b7 Michael Hanselmann
      m = key.match(name)
3178 691c81b7 Michael Hanselmann
      if m:
3179 691c81b7 Michael Hanselmann
        return (value, list(m.groups()))
3180 691c81b7 Michael Hanselmann
3181 691c81b7 Michael Hanselmann
  return None
3182 691c81b7 Michael Hanselmann
3183 691c81b7 Michael Hanselmann
3184 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
3185 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
3186 3f6a47a8 Michael Hanselmann

3187 3f6a47a8 Michael Hanselmann
  @type value: int
3188 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
3189 3f6a47a8 Michael Hanselmann
  @rtype: int
3190 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
3191 3f6a47a8 Michael Hanselmann

3192 3f6a47a8 Michael Hanselmann
  """
3193 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
3194 3f6a47a8 Michael Hanselmann
3195 3f6a47a8 Michael Hanselmann
3196 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
3197 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
3198 3f6a47a8 Michael Hanselmann

3199 3f6a47a8 Michael Hanselmann
  @type path: string
3200 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
3201 3f6a47a8 Michael Hanselmann
  @rtype: int
3202 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
3203 3f6a47a8 Michael Hanselmann

3204 3f6a47a8 Michael Hanselmann
  """
3205 3f6a47a8 Michael Hanselmann
  size = 0
3206 3f6a47a8 Michael Hanselmann
3207 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
3208 2a887df9 Michael Hanselmann
    for filename in files:
3209 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
3210 3f6a47a8 Michael Hanselmann
      size += st.st_size
3211 3f6a47a8 Michael Hanselmann
3212 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
3213 3f6a47a8 Michael Hanselmann
3214 3f6a47a8 Michael Hanselmann
3215 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
3216 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
3217 1b045f5d Balazs Lecz

3218 1b045f5d Balazs Lecz
  This function is Linux-specific.
3219 1b045f5d Balazs Lecz

3220 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
3221 1b045f5d Balazs Lecz
  @rtype: list of tuples
3222 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
3223 1b045f5d Balazs Lecz

3224 1b045f5d Balazs Lecz
  """
3225 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
3226 1b045f5d Balazs Lecz
  data = []
3227 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
3228 1b045f5d Balazs Lecz
  for line in mountlines:
3229 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
3230 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
3231 1b045f5d Balazs Lecz
3232 1b045f5d Balazs Lecz
  return data
3233 1b045f5d Balazs Lecz
3234 1b045f5d Balazs Lecz
3235 620a85fd Iustin Pop
def GetFilesystemStats(path):
3236 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
3237 3f6a47a8 Michael Hanselmann

3238 3f6a47a8 Michael Hanselmann
  @type path: string
3239 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
3240 3f6a47a8 Michael Hanselmann
  @rtype: int
3241 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
3242 3f6a47a8 Michael Hanselmann

3243 3f6a47a8 Michael Hanselmann
  """
3244 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
3245 3f6a47a8 Michael Hanselmann
3246 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
3247 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
3248 620a85fd Iustin Pop
  return (tsize, fsize)
3249 3f6a47a8 Michael Hanselmann
3250 3f6a47a8 Michael Hanselmann
3251 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
3252 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
3253 eb58f7bd Michael Hanselmann

3254 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
3255 eb58f7bd Michael Hanselmann

3256 eb58f7bd Michael Hanselmann
  @type fn: callable
3257 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
3258 bdefe5dd Michael Hanselmann
  @rtype: bool
3259 bdefe5dd Michael Hanselmann
  @return: Function's result
3260 eb58f7bd Michael Hanselmann

3261 eb58f7bd Michael Hanselmann
  """
3262 eb58f7bd Michael Hanselmann
  pid = os.fork()
3263 eb58f7bd Michael Hanselmann
  if pid == 0:
3264 eb58f7bd Michael Hanselmann
    # Child process
3265 eb58f7bd Michael Hanselmann
    try:
3266 82869978 Michael Hanselmann
      # In case the function uses temporary files
3267 82869978 Michael Hanselmann
      ResetTempfileModule()
3268 82869978 Michael Hanselmann
3269 eb58f7bd Michael Hanselmann
      # Call function
3270 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
3271 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
3272 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
3273 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
3274 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
3275 eb58f7bd Michael Hanselmann
      result = 33
3276 eb58f7bd Michael Hanselmann
3277 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
3278 eb58f7bd Michael Hanselmann
3279 eb58f7bd Michael Hanselmann
  # Parent process
3280 eb58f7bd Michael Hanselmann
3281 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
3282 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
3283 eb58f7bd Michael Hanselmann
3284 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
3285 eb58f7bd Michael Hanselmann
    exitcode = None
3286 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
3287 eb58f7bd Michael Hanselmann
  else:
3288 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
3289 eb58f7bd Michael Hanselmann
    signum = None
3290 eb58f7bd Michael Hanselmann
3291 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
3292 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
3293 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
3294 eb58f7bd Michael Hanselmann
3295 eb58f7bd Michael Hanselmann
  return bool(exitcode)
3296 eb58f7bd Michael Hanselmann
3297 eb58f7bd Michael Hanselmann
3298 560cbec1 Michael Hanselmann
def IgnoreProcessNotFound(fn, *args, **kwargs):
3299 560cbec1 Michael Hanselmann
  """Ignores ESRCH when calling a process-related function.
3300 560cbec1 Michael Hanselmann

3301 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
3302 560cbec1 Michael Hanselmann

3303 560cbec1 Michael Hanselmann
  @rtype: bool
3304 560cbec1 Michael Hanselmann
  @return: Whether process was found
3305 560cbec1 Michael Hanselmann

3306 560cbec1 Michael Hanselmann
  """
3307 560cbec1 Michael Hanselmann
  try:
3308 560cbec1 Michael Hanselmann
    fn(*args, **kwargs)
3309 560cbec1 Michael Hanselmann
  except EnvironmentError, err:
3310 560cbec1 Michael Hanselmann
    # Ignore ESRCH
3311 560cbec1 Michael Hanselmann
    if err.errno == errno.ESRCH:
3312 560cbec1 Michael Hanselmann
      return False
3313 560cbec1 Michael Hanselmann
    raise
3314 560cbec1 Michael Hanselmann
3315 560cbec1 Michael Hanselmann
  return True
3316 560cbec1 Michael Hanselmann
3317 560cbec1 Michael Hanselmann
3318 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
3319 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
3320 232144d0 Guido Trotter

3321 232144d0 Guido Trotter
  """
3322 232144d0 Guido Trotter
  try:
3323 232144d0 Guido Trotter
    return fn(*args, **kwargs)
3324 965d0e5b Guido Trotter
  except EnvironmentError, err:
3325 2fd7f564 Guido Trotter
    if err.errno == errno.EINTR:
3326 2fd7f564 Guido Trotter
      return None
3327 2fd7f564 Guido Trotter
    else:
3328 232144d0 Guido Trotter
      raise
3329 965d0e5b Guido Trotter
  except (select.error, socket.error), err:
3330 965d0e5b Guido Trotter
    # In python 2.6 and above select.error is an IOError, so it's handled
3331 965d0e5b Guido Trotter
    # above, in 2.5 and below it's not, and it's handled here.
3332 2fd7f564 Guido Trotter
    if err.args and err.args[0] == errno.EINTR:
3333 2fd7f564 Guido Trotter
      return None
3334 2fd7f564 Guido Trotter
    else:
3335 232144d0 Guido Trotter
      raise
3336 232144d0 Guido Trotter
3337 232144d0 Guido Trotter
3338 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
3339 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
3340 eb0f0ce0 Michael Hanselmann

3341 58885d79 Iustin Pop
  @type fd: int
3342 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
3343 58885d79 Iustin Pop

3344 eb0f0ce0 Michael Hanselmann
  """
3345 eb0f0ce0 Michael Hanselmann
  try:
3346 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
3347 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3348 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3349 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3350 eb0f0ce0 Michael Hanselmann
    raise
3351 de499029 Michael Hanselmann
3352 de499029 Michael Hanselmann
3353 3b813dd2 Iustin Pop
def FormatTime(val):
3354 3b813dd2 Iustin Pop
  """Formats a time value.
3355 3b813dd2 Iustin Pop

3356 3b813dd2 Iustin Pop
  @type val: float or None
3357 3b813dd2 Iustin Pop
  @param val: the timestamp as returned by time.time()
3358 3b813dd2 Iustin Pop
  @return: a string value or N/A if we don't have a valid timestamp
3359 3b813dd2 Iustin Pop

3360 3b813dd2 Iustin Pop
  """
3361 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3362 3b813dd2 Iustin Pop
    return "N/A"
3363 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3364 3b813dd2 Iustin Pop
  # platforms
3365 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3366 3b813dd2 Iustin Pop
3367 3b813dd2 Iustin Pop
3368 f8ea4ada Michael Hanselmann
def FormatSeconds(secs):
3369 f8ea4ada Michael Hanselmann
  """Formats seconds for easier reading.
3370 f8ea4ada Michael Hanselmann

3371 f8ea4ada Michael Hanselmann
  @type secs: number
3372 f8ea4ada Michael Hanselmann
  @param secs: Number of seconds
3373 f8ea4ada Michael Hanselmann
  @rtype: string
3374 f8ea4ada Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
3375 f8ea4ada Michael Hanselmann

3376 f8ea4ada Michael Hanselmann
  """
3377 f8ea4ada Michael Hanselmann
  parts = []
3378 f8ea4ada Michael Hanselmann
3379 f8ea4ada Michael Hanselmann
  secs = round(secs, 0)
3380 f8ea4ada Michael Hanselmann
3381 f8ea4ada Michael Hanselmann
  if secs > 0:
3382 f8ea4ada Michael Hanselmann
    # Negative values would be a bit tricky
3383 f8ea4ada Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
3384 f8ea4ada Michael Hanselmann
      (complete, secs) = divmod(secs, one)
3385 f8ea4ada Michael Hanselmann
      if complete or parts:
3386 f8ea4ada Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
3387 f8ea4ada Michael Hanselmann
3388 f8ea4ada Michael Hanselmann
  parts.append("%ds" % secs)
3389 f8ea4ada Michael Hanselmann
3390 f8ea4ada Michael Hanselmann
  return " ".join(parts)
3391 f8ea4ada Michael Hanselmann
3392 f8ea4ada Michael Hanselmann
3393 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
3394 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
3395 05e50653 Michael Hanselmann

3396 5cbe43a5 Michael Hanselmann
  @type filename: string
3397 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3398 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3399 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3400 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3401 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3402 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3403 5cbe43a5 Michael Hanselmann

3404 05e50653 Michael Hanselmann
  """
3405 05e50653 Michael Hanselmann
  if now is None:
3406 05e50653 Michael Hanselmann
    now = time.time()
3407 05e50653 Michael Hanselmann
3408 05e50653 Michael Hanselmann
  try:
3409 05e50653 Michael Hanselmann
    value = ReadFile(filename)
3410 05e50653 Michael Hanselmann
  except IOError, err:
3411 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
3412 05e50653 Michael Hanselmann
      raise
3413 05e50653 Michael Hanselmann
    value = None
3414 05e50653 Michael Hanselmann
3415 05e50653 Michael Hanselmann
  if value is not None:
3416 05e50653 Michael Hanselmann
    try:
3417 05e50653 Michael Hanselmann
      value = int(value)
3418 05e50653 Michael Hanselmann
    except ValueError:
3419 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
3420 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
3421 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
3422 05e50653 Michael Hanselmann
      value = None
3423 05e50653 Michael Hanselmann
3424 05e50653 Michael Hanselmann
    if value is not None:
3425 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
3426 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
3427 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
3428 5cbe43a5 Michael Hanselmann
        value = None
3429 5cbe43a5 Michael Hanselmann
3430 5cbe43a5 Michael Hanselmann
      elif now > value:
3431 05e50653 Michael Hanselmann
        value = None
3432 05e50653 Michael Hanselmann
3433 05e50653 Michael Hanselmann
  return value
3434 05e50653 Michael Hanselmann
3435 05e50653 Michael Hanselmann
3436 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
3437 de0ea66b Michael Hanselmann
  """Retry loop timed out.
3438 de0ea66b Michael Hanselmann

3439 40bc67e1 Guido Trotter
  Any arguments which was passed by the retried function to RetryAgain will be
3440 40bc67e1 Guido Trotter
  preserved in RetryTimeout, if it is raised. If such argument was an exception
3441 40bc67e1 Guido Trotter
  the RaiseInner helper method will reraise it.
3442 40bc67e1 Guido Trotter

3443 de0ea66b Michael Hanselmann
  """
3444 506be7c5 Guido Trotter
  def RaiseInner(self):
3445 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3446 506be7c5 Guido Trotter
      raise self.args[0]
3447 506be7c5 Guido Trotter
    else:
3448 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3449 de0ea66b Michael Hanselmann
3450 de0ea66b Michael Hanselmann
3451 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3452 de0ea66b Michael Hanselmann
  """Retry again.
3453 de0ea66b Michael Hanselmann

3454 40bc67e1 Guido Trotter
  Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as
3455 40bc67e1 Guido Trotter
  arguments to RetryTimeout. If an exception is passed, the RaiseInner() method
3456 40bc67e1 Guido Trotter
  of the RetryTimeout() method can be used to reraise it.
3457 40bc67e1 Guido Trotter

3458 de0ea66b Michael Hanselmann
  """
3459 de0ea66b Michael Hanselmann
3460 de0ea66b Michael Hanselmann
3461 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3462 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3463 de0ea66b Michael Hanselmann

3464 de0ea66b Michael Hanselmann
  """
3465 de0ea66b Michael Hanselmann
  __slots__ = [
3466 de0ea66b Michael Hanselmann
    "_factor",
3467 de0ea66b Michael Hanselmann
    "_limit",
3468 de0ea66b Michael Hanselmann
    "_next",
3469 de0ea66b Michael Hanselmann
    "_start",
3470 de0ea66b Michael Hanselmann
    ]
3471 de0ea66b Michael Hanselmann
3472 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3473 de0ea66b Michael Hanselmann
    """Initializes this class.
3474 de0ea66b Michael Hanselmann

3475 de0ea66b Michael Hanselmann
    @type start: float
3476 de0ea66b Michael Hanselmann
    @param start: Initial delay
3477 de0ea66b Michael Hanselmann
    @type factor: float
3478 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3479 de0ea66b Michael Hanselmann
    @type limit: float or None
3480 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3481 de0ea66b Michael Hanselmann

3482 de0ea66b Michael Hanselmann
    """
3483 de0ea66b Michael Hanselmann
    assert start > 0.0
3484 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3485 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3486 de0ea66b Michael Hanselmann
3487 de0ea66b Michael Hanselmann
    self._start = start
3488 de0ea66b Michael Hanselmann
    self._factor = factor
3489 de0ea66b Michael Hanselmann
    self._limit = limit
3490 de0ea66b Michael Hanselmann
3491 de0ea66b Michael Hanselmann
    self._next = start
3492 de0ea66b Michael Hanselmann
3493 de0ea66b Michael Hanselmann
  def __call__(self):
3494 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3495 de0ea66b Michael Hanselmann

3496 de0ea66b Michael Hanselmann
    """
3497 de0ea66b Michael Hanselmann
    current = self._next
3498 de0ea66b Michael Hanselmann
3499 de0ea66b Michael Hanselmann
    # Update for next run
3500 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3501 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3502 de0ea66b Michael Hanselmann
3503 de0ea66b Michael Hanselmann
    return current
3504 de0ea66b Michael Hanselmann
3505 de0ea66b Michael Hanselmann
3506 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3507 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3508 de0ea66b Michael Hanselmann
3509 de0ea66b Michael Hanselmann
3510 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3511 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3512 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3513 de0ea66b Michael Hanselmann

3514 de0ea66b Michael Hanselmann
  The function C{fn} is called repeatedly until it doesn't throw L{RetryAgain}
3515 de0ea66b Michael Hanselmann
  anymore. Between calls a delay, specified by C{delay}, is inserted. After a
3516 de0ea66b Michael Hanselmann
  total of C{timeout} seconds, this function throws L{RetryTimeout}.
3517 de0ea66b Michael Hanselmann

3518 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3519 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3520 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3521 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3522 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3523 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3524 de0ea66b Michael Hanselmann

3525 de0ea66b Michael Hanselmann
  @type fn: callable
3526 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3527 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3528 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3529 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3530 de0ea66b Michael Hanselmann
  @type timeout: float
3531 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3532 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3533 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3534 de0ea66b Michael Hanselmann
  @return: Return value of function
3535 de0ea66b Michael Hanselmann

3536 de0ea66b Michael Hanselmann
  """
3537 de0ea66b Michael Hanselmann
  assert callable(fn)
3538 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
3539 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
3540 de0ea66b Michael Hanselmann
3541 de0ea66b Michael Hanselmann
  if args is None:
3542 de0ea66b Michael Hanselmann
    args = []
3543 de0ea66b Michael Hanselmann
3544 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
3545 de0ea66b Michael Hanselmann
3546 de0ea66b Michael Hanselmann
  if callable(delay):
3547 de0ea66b Michael Hanselmann
    # External function to calculate delay
3548 de0ea66b Michael Hanselmann
    calc_delay = delay
3549 de0ea66b Michael Hanselmann
3550 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
3551 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
3552 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
3553 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
3554 de0ea66b Michael Hanselmann
3555 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
3556 de0ea66b Michael Hanselmann
    # Always use the remaining time
3557 de0ea66b Michael Hanselmann
    calc_delay = None
3558 de0ea66b Michael Hanselmann
3559 de0ea66b Michael Hanselmann
  else:
3560 de0ea66b Michael Hanselmann
    # Static delay
3561 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
3562 de0ea66b Michael Hanselmann
3563 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
3564 de0ea66b Michael Hanselmann
3565 de0ea66b Michael Hanselmann
  while True:
3566 506be7c5 Guido Trotter
    retry_args = []
3567 de0ea66b Michael Hanselmann
    try:
3568 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
3569 de0ea66b Michael Hanselmann
      return fn(*args)
3570 506be7c5 Guido Trotter
    except RetryAgain, err:
3571 506be7c5 Guido Trotter
      retry_args = err.args
3572 1b429e2a Iustin Pop
    except RetryTimeout:
3573 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
3574 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
3575 de0ea66b Michael Hanselmann
3576 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
3577 de0ea66b Michael Hanselmann
3578 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
3579 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
3580 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
3581 de0ea66b Michael Hanselmann
3582 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
3583 de0ea66b Michael Hanselmann
3584 de0ea66b Michael Hanselmann
    if calc_delay is None:
3585 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
3586 de0ea66b Michael Hanselmann
    else:
3587 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
3588 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
3589 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
3590 de0ea66b Michael Hanselmann
3591 de0ea66b Michael Hanselmann
3592 bdd5e420 Michael Hanselmann
def GetClosedTempfile(*args, **kwargs):
3593 bdd5e420 Michael Hanselmann
  """Creates a temporary file and returns its path.
3594 a55474c7 Michael Hanselmann

3595 bdd5e420 Michael Hanselmann
  """
3596 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3597 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3598 bdd5e420 Michael Hanselmann
  return path
3599 bdd5e420 Michael Hanselmann
3600 bdd5e420 Michael Hanselmann
3601 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3602 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3603 bdd5e420 Michael Hanselmann

3604 bdd5e420 Michael Hanselmann
  @type common_name: string
3605 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3606 a55474c7 Michael Hanselmann
  @type validity: int
3607 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3608 a55474c7 Michael Hanselmann

3609 a55474c7 Michael Hanselmann
  """
3610 bdd5e420 Michael Hanselmann
  # Create private and public key
3611 bdd5e420 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
3612 bdd5e420 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
3613 bdd5e420 Michael Hanselmann
3614 bdd5e420 Michael Hanselmann
  # Create self-signed certificate
3615 bdd5e420 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
3616 bdd5e420 Michael Hanselmann
  if common_name:
3617 bdd5e420 Michael Hanselmann
    cert.get_subject().CN = common_name
3618 bdd5e420 Michael Hanselmann
  cert.set_serial_number(1)
3619 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
3620 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
3621 bdd5e420 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
3622 bdd5e420 Michael Hanselmann
  cert.set_pubkey(key)
3623 bdd5e420 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
3624 bdd5e420 Michael Hanselmann
3625 bdd5e420 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
3626 bdd5e420 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3627 bdd5e420 Michael Hanselmann
3628 bdd5e420 Michael Hanselmann
  return (key_pem, cert_pem)
3629 bdd5e420 Michael Hanselmann
3630 bdd5e420 Michael Hanselmann
3631 600535f0 Manuel Franceschini
def GenerateSelfSignedSslCert(filename, common_name=constants.X509_CERT_CN,
3632 600535f0 Manuel Franceschini
                              validity=constants.X509_CERT_DEFAULT_VALIDITY):
3633 bdd5e420 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
3634 bdd5e420 Michael Hanselmann

3635 2ea65c7d Manuel Franceschini
  @type filename: str
3636 2ea65c7d Manuel Franceschini
  @param filename: path to write certificate to
3637 600535f0 Manuel Franceschini
  @type common_name: string
3638 600535f0 Manuel Franceschini
  @param common_name: commonName value
3639 600535f0 Manuel Franceschini
  @type validity: int
3640 600535f0 Manuel Franceschini
  @param validity: validity of certificate in number of days
3641 600535f0 Manuel Franceschini

3642 bdd5e420 Michael Hanselmann
  """
3643 600535f0 Manuel Franceschini
  # TODO: Investigate using the cluster name instead of X505_CERT_CN for
3644 600535f0 Manuel Franceschini
  # common_name, as cluster-renames are very seldom, and it'd be nice if RAPI
3645 600535f0 Manuel Franceschini
  # and node daemon certificates have the proper Subject/Issuer.
3646 600535f0 Manuel Franceschini
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(common_name,
3647 bdd5e420 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
3648 bdd5e420 Michael Hanselmann
3649 bdd5e420 Michael Hanselmann
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
3650 a55474c7 Michael Hanselmann
3651 a55474c7 Michael Hanselmann
3652 a87b4824 Michael Hanselmann
class FileLock(object):
3653 a87b4824 Michael Hanselmann
  """Utility class for file locks.
3654 a87b4824 Michael Hanselmann

3655 a87b4824 Michael Hanselmann
  """
3656 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3657 58885d79 Iustin Pop
    """Constructor for FileLock.
3658 58885d79 Iustin Pop

3659 b4478d34 Michael Hanselmann
    @type fd: file
3660 b4478d34 Michael Hanselmann
    @param fd: File object
3661 58885d79 Iustin Pop
    @type filename: str
3662 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3663 58885d79 Iustin Pop

3664 58885d79 Iustin Pop
    """
3665 b4478d34 Michael Hanselmann
    self.fd = fd
3666 a87b4824 Michael Hanselmann
    self.filename = filename
3667 b4478d34 Michael Hanselmann
3668 b4478d34 Michael Hanselmann
  @classmethod
3669 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3670 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3671 b4478d34 Michael Hanselmann

3672 b4478d34 Michael Hanselmann
    @type filename: string
3673 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3674 b4478d34 Michael Hanselmann

3675 b4478d34 Michael Hanselmann
    """
3676 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3677 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3678 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3679 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3680 b4478d34 Michael Hanselmann
               filename)
3681 a87b4824 Michael Hanselmann
3682 a87b4824 Michael Hanselmann
  def __del__(self):
3683 a87b4824 Michael Hanselmann
    self.Close()
3684 a87b4824 Michael Hanselmann
3685 a87b4824 Michael Hanselmann
  def Close(self):
3686 58885d79 Iustin Pop
    """Close the file and release the lock.
3687 58885d79 Iustin Pop

3688 58885d79 Iustin Pop
    """
3689 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3690 a87b4824 Michael Hanselmann
      self.fd.close()
3691 a87b4824 Michael Hanselmann
      self.fd = None
3692 a87b4824 Michael Hanselmann
3693 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3694 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3695 aa74b828 Michael Hanselmann

3696 aa74b828 Michael Hanselmann
    @type flag: int
3697 58885d79 Iustin Pop
    @param flag: operation flag
3698 aa74b828 Michael Hanselmann
    @type blocking: bool
3699 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3700 aa74b828 Michael Hanselmann
    @type timeout: None or float
3701 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3702 aa74b828 Michael Hanselmann
                    non-blocking mode).
3703 aa74b828 Michael Hanselmann
    @type errmsg: string
3704 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3705 aa74b828 Michael Hanselmann

3706 aa74b828 Michael Hanselmann
    """
3707 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
3708 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
3709 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
3710 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
3711 a87b4824 Michael Hanselmann
3712 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
3713 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
3714 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
3715 a87b4824 Michael Hanselmann
3716 cc4c9b91 Michael Hanselmann
    if timeout is None:
3717 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
3718 cc4c9b91 Michael Hanselmann
    else:
3719 cc4c9b91 Michael Hanselmann
      try:
3720 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
3721 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
3722 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
3723 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
3724 aa74b828 Michael Hanselmann
3725 cc4c9b91 Michael Hanselmann
  @staticmethod
3726 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
3727 cc4c9b91 Michael Hanselmann
    try:
3728 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
3729 cc4c9b91 Michael Hanselmann
    except IOError, err:
3730 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
3731 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
3732 31892b4c Michael Hanselmann
3733 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
3734 cc4c9b91 Michael Hanselmann
      raise
3735 aa74b828 Michael Hanselmann
3736 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
3737 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
3738 a87b4824 Michael Hanselmann

3739 58885d79 Iustin Pop
    @type blocking: boolean
3740 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3741 58885d79 Iustin Pop
        can lock the file or return immediately
3742 58885d79 Iustin Pop
    @type timeout: int or None
3743 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3744 58885d79 Iustin Pop
        (in blocking mode)
3745 58885d79 Iustin Pop

3746 a87b4824 Michael Hanselmann
    """
3747 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3748 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3749 a87b4824 Michael Hanselmann
3750 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3751 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3752 a87b4824 Michael Hanselmann

3753 58885d79 Iustin Pop
    @type blocking: boolean
3754 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3755 58885d79 Iustin Pop
        can lock the file or return immediately
3756 58885d79 Iustin Pop
    @type timeout: int or None
3757 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3758 58885d79 Iustin Pop
        (in blocking mode)
3759 58885d79 Iustin Pop

3760 a87b4824 Michael Hanselmann
    """
3761 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3762 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3763 a87b4824 Michael Hanselmann
3764 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3765 a87b4824 Michael Hanselmann
    """Unlocks the file.
3766 a87b4824 Michael Hanselmann

3767 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3768 58885d79 Iustin Pop
    operation::
3769 58885d79 Iustin Pop

3770 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3771 58885d79 Iustin Pop
      operations.
3772 58885d79 Iustin Pop

3773 58885d79 Iustin Pop
    @type blocking: boolean
3774 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3775 58885d79 Iustin Pop
        can lock the file or return immediately
3776 58885d79 Iustin Pop
    @type timeout: int or None
3777 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3778 58885d79 Iustin Pop
        (in blocking mode)
3779 a87b4824 Michael Hanselmann

3780 a87b4824 Michael Hanselmann
    """
3781 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3782 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3783 a87b4824 Michael Hanselmann
3784 a87b4824 Michael Hanselmann
3785 339be5a8 Michael Hanselmann
class LineSplitter:
3786 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3787 339be5a8 Michael Hanselmann

3788 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3789 339be5a8 Michael Hanselmann

3790 339be5a8 Michael Hanselmann
  """
3791 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3792 339be5a8 Michael Hanselmann
    """Initializes this class.
3793 339be5a8 Michael Hanselmann

3794 339be5a8 Michael Hanselmann
    @type line_fn: callable
3795 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3796 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3797 339be5a8 Michael Hanselmann

3798 339be5a8 Michael Hanselmann
    """
3799 339be5a8 Michael Hanselmann
    assert callable(line_fn)
3800 339be5a8 Michael Hanselmann
3801 339be5a8 Michael Hanselmann
    if args:
3802 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
3803 339be5a8 Michael Hanselmann
      self._line_fn = \
3804 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
3805 339be5a8 Michael Hanselmann
    else:
3806 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
3807 339be5a8 Michael Hanselmann
3808 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
3809 339be5a8 Michael Hanselmann
    self._buffer = ""
3810 339be5a8 Michael Hanselmann
3811 339be5a8 Michael Hanselmann
  def write(self, data):
3812 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
3813 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
3814 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
3815 339be5a8 Michael Hanselmann
3816 339be5a8 Michael Hanselmann
  def flush(self):
3817 339be5a8 Michael Hanselmann
    while self._lines:
3818 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
3819 339be5a8 Michael Hanselmann
3820 339be5a8 Michael Hanselmann
  def close(self):
3821 339be5a8 Michael Hanselmann
    self.flush()
3822 339be5a8 Michael Hanselmann
    if self._buffer:
3823 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
3824 339be5a8 Michael Hanselmann
3825 339be5a8 Michael Hanselmann
3826 451575de Guido Trotter
def SignalHandled(signums):
3827 451575de Guido Trotter
  """Signal Handled decoration.
3828 451575de Guido Trotter

3829 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3830 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3831 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3832 451575de Guido Trotter
  objects as values.
3833 451575de Guido Trotter

3834 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3835 451575de Guido Trotter
  with different handlers.
3836 451575de Guido Trotter

3837 451575de Guido Trotter
  @type signums: list
3838 451575de Guido Trotter
  @param signums: signals to intercept
3839 451575de Guido Trotter

3840 451575de Guido Trotter
  """
3841 451575de Guido Trotter
  def wrap(fn):
3842 451575de Guido Trotter
    def sig_function(*args, **kwargs):
3843 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
3844 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
3845 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
3846 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
3847 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
3848 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
3849 451575de Guido Trotter
      else:
3850 451575de Guido Trotter
        signal_handlers = {}
3851 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
3852 451575de Guido Trotter
      sighandler = SignalHandler(signums)
3853 451575de Guido Trotter
      try:
3854 451575de Guido Trotter
        for sig in signums:
3855 451575de Guido Trotter
          signal_handlers[sig] = sighandler
3856 451575de Guido Trotter
        return fn(*args, **kwargs)
3857 451575de Guido Trotter
      finally:
3858 451575de Guido Trotter
        sighandler.Reset()
3859 451575de Guido Trotter
    return sig_function
3860 451575de Guido Trotter
  return wrap
3861 451575de Guido Trotter
3862 451575de Guido Trotter
3863 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
3864 b9768937 Michael Hanselmann
  try:
3865 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
3866 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
3867 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
3868 b9768937 Michael Hanselmann
  except AttributeError:
3869 b9768937 Michael Hanselmann
    # Not supported
3870 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, _): # pylint: disable-msg=R0201
3871 b9768937 Michael Hanselmann
      return -1
3872 b9768937 Michael Hanselmann
  else:
3873 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
3874 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
3875 b9768937 Michael Hanselmann
3876 b9768937 Michael Hanselmann
  def __init__(self):
3877 b9768937 Michael Hanselmann
    """Initializes this class.
3878 b9768937 Michael Hanselmann

3879 b9768937 Michael Hanselmann
    """
3880 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
3881 b9768937 Michael Hanselmann
3882 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
3883 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
3884 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
3885 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
3886 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
3887 b9768937 Michael Hanselmann
3888 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
3889 b9768937 Michael Hanselmann
3890 b9768937 Michael Hanselmann
    # Utility functions
3891 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
3892 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
3893 b9768937 Michael Hanselmann
3894 b9768937 Michael Hanselmann
  def Reset(self):
3895 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
3896 b9768937 Michael Hanselmann

3897 b9768937 Michael Hanselmann
    """
3898 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3899 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3900 b9768937 Michael Hanselmann
      self._previous = None
3901 b9768937 Michael Hanselmann
3902 b9768937 Michael Hanselmann
  def Notify(self):
3903 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3904 b9768937 Michael Hanselmann

3905 b9768937 Michael Hanselmann
    """
3906 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3907 b9768937 Michael Hanselmann
3908 b9768937 Michael Hanselmann
  def __del__(self):
3909 b9768937 Michael Hanselmann
    """Called before object deletion.
3910 b9768937 Michael Hanselmann

3911 b9768937 Michael Hanselmann
    """
3912 b9768937 Michael Hanselmann
    self.Reset()
3913 b9768937 Michael Hanselmann
3914 b9768937 Michael Hanselmann
3915 de499029 Michael Hanselmann
class SignalHandler(object):
3916 de499029 Michael Hanselmann
  """Generic signal handler class.
3917 de499029 Michael Hanselmann

3918 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3919 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3920 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3921 58885d79 Iustin Pop
  signal was sent.
3922 58885d79 Iustin Pop

3923 58885d79 Iustin Pop
  @type signum: list
3924 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3925 58885d79 Iustin Pop
  @type called: boolean
3926 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3927 de499029 Michael Hanselmann

3928 de499029 Michael Hanselmann
  """
3929 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3930 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3931 de499029 Michael Hanselmann

3932 58885d79 Iustin Pop
    @type signum: int or list of ints
3933 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3934 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3935 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3936 de499029 Michael Hanselmann

3937 de499029 Michael Hanselmann
    """
3938 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
3939 92b61ec7 Michael Hanselmann
3940 6c52849e Guido Trotter
    self.signum = set(signum)
3941 de499029 Michael Hanselmann
    self.called = False
3942 de499029 Michael Hanselmann
3943 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
3944 b9768937 Michael Hanselmann
    self._wakeup = wakeup
3945 92b61ec7 Michael Hanselmann
3946 de499029 Michael Hanselmann
    self._previous = {}
3947 de499029 Michael Hanselmann
    try:
3948 de499029 Michael Hanselmann
      for signum in self.signum:
3949 de499029 Michael Hanselmann
        # Setup handler
3950 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3951 de499029 Michael Hanselmann
        try:
3952 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3953 de499029 Michael Hanselmann
        except:
3954 de499029 Michael Hanselmann
          # Restore previous handler
3955 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3956 de499029 Michael Hanselmann
          raise
3957 de499029 Michael Hanselmann
    except:
3958 de499029 Michael Hanselmann
      # Reset all handlers
3959 de499029 Michael Hanselmann
      self.Reset()
3960 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3961 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3962 de499029 Michael Hanselmann
      raise
3963 de499029 Michael Hanselmann
3964 de499029 Michael Hanselmann
  def __del__(self):
3965 de499029 Michael Hanselmann
    self.Reset()
3966 de499029 Michael Hanselmann
3967 de499029 Michael Hanselmann
  def Reset(self):
3968 de499029 Michael Hanselmann
    """Restore previous handler.
3969 de499029 Michael Hanselmann

3970 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3971 58885d79 Iustin Pop

3972 de499029 Michael Hanselmann
    """
3973 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3974 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3975 de499029 Michael Hanselmann
      # If successful, remove from dict
3976 de499029 Michael Hanselmann
      del self._previous[signum]
3977 de499029 Michael Hanselmann
3978 de499029 Michael Hanselmann
  def Clear(self):
3979 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3980 de499029 Michael Hanselmann

3981 de499029 Michael Hanselmann
    This function can be used in case a signal may arrive several times.
3982 de499029 Michael Hanselmann

3983 de499029 Michael Hanselmann
    """
3984 de499029 Michael Hanselmann
    self.called = False
3985 de499029 Michael Hanselmann
3986 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3987 de499029 Michael Hanselmann
    """Actual signal handling function.
3988 de499029 Michael Hanselmann

3989 de499029 Michael Hanselmann
    """
3990 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3991 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3992 de499029 Michael Hanselmann
    self.called = True
3993 a2d2e1a7 Iustin Pop
3994 b9768937 Michael Hanselmann
    if self._wakeup:
3995 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
3996 b9768937 Michael Hanselmann
      self._wakeup.Notify()
3997 b9768937 Michael Hanselmann
3998 92b61ec7 Michael Hanselmann
    if self._handler_fn:
3999 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
4000 92b61ec7 Michael Hanselmann
4001 a2d2e1a7 Iustin Pop
4002 a2d2e1a7 Iustin Pop
class FieldSet(object):
4003 a2d2e1a7 Iustin Pop
  """A simple field set.
4004 a2d2e1a7 Iustin Pop

4005 a2d2e1a7 Iustin Pop
  Among the features are:
4006 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
4007 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
4008 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
4009 a2d2e1a7 Iustin Pop

4010 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
4011 a2d2e1a7 Iustin Pop

4012 a2d2e1a7 Iustin Pop
  """
4013 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
4014 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
4015 a2d2e1a7 Iustin Pop
4016 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
4017 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
4018 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
4019 a2d2e1a7 Iustin Pop
4020 a2d2e1a7 Iustin Pop
  def Matches(self, field):
4021 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
4022 a2d2e1a7 Iustin Pop

4023 a2d2e1a7 Iustin Pop
    @type field: str
4024 a2d2e1a7 Iustin Pop
    @param field: the string to match
4025 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
4026 a2d2e1a7 Iustin Pop

4027 a2d2e1a7 Iustin Pop
    """
4028 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
4029 a2d2e1a7 Iustin Pop
      return m
4030 6c881c52 Iustin Pop
    return None
4031 a2d2e1a7 Iustin Pop
4032 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
4033 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
4034 a2d2e1a7 Iustin Pop

4035 a2d2e1a7 Iustin Pop
    @type items: list
4036 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
4037 a2d2e1a7 Iustin Pop
    @rtype: list
4038 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
4039 a2d2e1a7 Iustin Pop

4040 a2d2e1a7 Iustin Pop
    """
4041 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]
4042 557838c1 René Nussbaumer
4043 557838c1 René Nussbaumer
4044 557838c1 René Nussbaumer
class RunningTimeout(object):
4045 557838c1 René Nussbaumer
  """Class to calculate remaining timeout when doing several operations.
4046 557838c1 René Nussbaumer

4047 557838c1 René Nussbaumer
  """
4048 557838c1 René Nussbaumer
  __slots__ = [
4049 557838c1 René Nussbaumer
    "_allow_negative",
4050 557838c1 René Nussbaumer
    "_start_time",
4051 557838c1 René Nussbaumer
    "_time_fn",
4052 557838c1 René Nussbaumer
    "_timeout",
4053 557838c1 René Nussbaumer
    ]
4054 557838c1 René Nussbaumer
4055 557838c1 René Nussbaumer
  def __init__(self, timeout, allow_negative, _time_fn=time.time):
4056 557838c1 René Nussbaumer
    """Initializes this class.
4057 557838c1 René Nussbaumer

4058 557838c1 René Nussbaumer
    @type timeout: float
4059 557838c1 René Nussbaumer
    @param timeout: Timeout duration
4060 557838c1 René Nussbaumer
    @type allow_negative: bool
4061 557838c1 René Nussbaumer
    @param allow_negative: Whether to return values below zero
4062 557838c1 René Nussbaumer
    @param _time_fn: Time function for unittests
4063 557838c1 René Nussbaumer

4064 557838c1 René Nussbaumer
    """
4065 557838c1 René Nussbaumer
    object.__init__(self)
4066 557838c1 René Nussbaumer
4067 557838c1 René Nussbaumer
    if timeout is not None and timeout < 0.0:
4068 557838c1 René Nussbaumer
      raise ValueError("Timeout must not be negative")
4069 557838c1 René Nussbaumer
4070 557838c1 René Nussbaumer
    self._timeout = timeout
4071 557838c1 René Nussbaumer
    self._allow_negative = allow_negative
4072 557838c1 René Nussbaumer
    self._time_fn = _time_fn
4073 557838c1 René Nussbaumer
4074 557838c1 René Nussbaumer
    self._start_time = None
4075 557838c1 René Nussbaumer
4076 557838c1 René Nussbaumer
  def Remaining(self):
4077 557838c1 René Nussbaumer
    """Returns the remaining timeout.
4078 557838c1 René Nussbaumer

4079 557838c1 René Nussbaumer
    """
4080 557838c1 René Nussbaumer
    if self._timeout is None:
4081 557838c1 René Nussbaumer
      return None
4082 557838c1 René Nussbaumer
4083 557838c1 René Nussbaumer
    # Get start time on first calculation
4084 557838c1 René Nussbaumer
    if self._start_time is None:
4085 557838c1 René Nussbaumer
      self._start_time = self._time_fn()
4086 557838c1 René Nussbaumer
4087 557838c1 René Nussbaumer
    # Calculate remaining time
4088 557838c1 René Nussbaumer
    remaining_timeout = self._start_time + self._timeout - self._time_fn()
4089 557838c1 René Nussbaumer
4090 557838c1 René Nussbaumer
    if not self._allow_negative:
4091 557838c1 René Nussbaumer
      # Ensure timeout is always >= 0
4092 557838c1 René Nussbaumer
      return max(0.0, remaining_timeout)
4093 557838c1 René Nussbaumer
4094 557838c1 René Nussbaumer
    return remaining_timeout