Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ dd94e9f6

History | View | Annotate | Download (110.4 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 153533f3 Michael Hanselmann
_SORTER_RE = re.compile("^%s(.*)$" % (8 * "(\D+|\d+)?"))
113 153533f3 Michael Hanselmann
_SORTER_DIGIT = re.compile("^\d+$")
114 153533f3 Michael Hanselmann
115 7c0d6283 Michael Hanselmann
116 a8083063 Iustin Pop
class RunResult(object):
117 58885d79 Iustin Pop
  """Holds the result of running external programs.
118 58885d79 Iustin Pop

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

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

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

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

199 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
200 a8083063 Iustin Pop
  closed.
201 a8083063 Iustin Pop

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

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

276 0260032c Iustin Pop
  This should be called between the first and second fork, due to
277 0260032c Iustin Pop
  setsid usage.
278 0260032c Iustin Pop

279 0260032c Iustin Pop
  @param cwd: the directory to which to chdir
280 0260032c Iustin Pop
  @param umask: the umask to setup
281 0260032c Iustin Pop

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

291 79634555 Iustin Pop
  @param output_file: if not None, the file to which to redirect
292 79634555 Iustin Pop
      stdout/stderr
293 79634555 Iustin Pop
  @param output_fd: if not None, the file descriptor for stdout/stderr
294 79634555 Iustin Pop

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

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

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

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

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

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

499 c74cda62 René Nussbaumer
  @raises RetryAgain: If child is still alive
500 c74cda62 René Nussbaumer

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

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

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

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

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

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

679 73027ed2 Michael Hanselmann
  @type fd: int
680 73027ed2 Michael Hanselmann
  @param fd: File descriptor
681 73027ed2 Michael Hanselmann
  @type enable: bool
682 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
683 73027ed2 Michael Hanselmann

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

698 287a1740 Michael Hanselmann
  @type fd: int
699 287a1740 Michael Hanselmann
  @param fd: File descriptor
700 287a1740 Michael Hanselmann
  @type enable: bool
701 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
702 287a1740 Michael Hanselmann

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

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

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

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

771 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
772 a8083063 Iustin Pop
  errors are passed.
773 a8083063 Iustin Pop

774 58885d79 Iustin Pop
  @type filename: str
775 58885d79 Iustin Pop
  @param filename: the file to be removed
776 58885d79 Iustin Pop

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

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

792 72087dcd Balazs Lecz
  @type dirname: str
793 72087dcd Balazs Lecz
  @param dirname: the empty directory to be removed
794 72087dcd Balazs Lecz

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

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

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

834 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
835 76e5f8b5 Michael Hanselmann
  before Python 2.5.
836 76e5f8b5 Michael Hanselmann

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

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

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

873 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
874 a8083063 Iustin Pop
  instead.
875 a8083063 Iustin Pop

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

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

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

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

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

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

991 a01b500b Michael Hanselmann
  @type pid: int
992 a01b500b Michael Hanselmann
  @param pid: Process ID
993 a01b500b Michael Hanselmann
  @rtype: string
994 a01b500b Michael Hanselmann

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

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

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

1037 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
1038 a01b500b Michael Hanselmann
  function.
1039 a01b500b Michael Hanselmann

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

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

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

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

1092 a01b500b Michael Hanselmann
  @type pid: int
1093 a01b500b Michael Hanselmann
  @param pid: Process ID
1094 a01b500b Michael Hanselmann
  @type signum: int
1095 a01b500b Michael Hanselmann
  @param signum: Signal number
1096 a01b500b Michael Hanselmann
  @rtype: bool
1097 a01b500b Michael Hanselmann

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

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

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

1147 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
1148 debed9ae Michael Hanselmann

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

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

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

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

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

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

1224 28f34048 Michael Hanselmann
  @type name: number or string
1225 28f34048 Michael Hanselmann
  @param name: Service name or port specification
1226 28f34048 Michael Hanselmann

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

1248 58885d79 Iustin Pop
  @rtype: dict
1249 58885d79 Iustin Pop
  @return:
1250 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1251 58885d79 Iustin Pop
       the size of the volume
1252 a8083063 Iustin Pop

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

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

1281 a8083063 Iustin Pop
  """
1282 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
1283 a8083063 Iustin Pop
1284 a8083063 Iustin Pop
1285 153533f3 Michael Hanselmann
def _NiceSortTryInt(val):
1286 153533f3 Michael Hanselmann
  """Attempts to convert a string to an integer.
1287 153533f3 Michael Hanselmann

1288 153533f3 Michael Hanselmann
  """
1289 153533f3 Michael Hanselmann
  if val and _SORTER_DIGIT.match(val):
1290 153533f3 Michael Hanselmann
    return int(val)
1291 153533f3 Michael Hanselmann
  else:
1292 153533f3 Michael Hanselmann
    return val
1293 153533f3 Michael Hanselmann
1294 153533f3 Michael Hanselmann
1295 153533f3 Michael Hanselmann
def _NiceSortKey(value):
1296 153533f3 Michael Hanselmann
  """Extract key for sorting.
1297 153533f3 Michael Hanselmann

1298 153533f3 Michael Hanselmann
  """
1299 153533f3 Michael Hanselmann
  return [_NiceSortTryInt(grp)
1300 153533f3 Michael Hanselmann
          for grp in _SORTER_RE.match(value).groups()]
1301 153533f3 Michael Hanselmann
1302 153533f3 Michael Hanselmann
1303 153533f3 Michael Hanselmann
def NiceSort(values, key=None):
1304 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
1305 a8083063 Iustin Pop

1306 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
1307 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
1308 58885d79 Iustin Pop
  'a11']}.
1309 a8083063 Iustin Pop

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

1314 153533f3 Michael Hanselmann
  @type values: list
1315 153533f3 Michael Hanselmann
  @param values: the names to be sorted
1316 153533f3 Michael Hanselmann
  @type key: callable or None
1317 153533f3 Michael Hanselmann
  @param key: function of one argument to extract a comparison key from each
1318 153533f3 Michael Hanselmann
    list element, must return string
1319 58885d79 Iustin Pop
  @rtype: list
1320 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
1321 a8083063 Iustin Pop

1322 a8083063 Iustin Pop
  """
1323 153533f3 Michael Hanselmann
  if key is None:
1324 153533f3 Michael Hanselmann
    keyfunc = _NiceSortKey
1325 153533f3 Michael Hanselmann
  else:
1326 153533f3 Michael Hanselmann
    keyfunc = lambda value: _NiceSortKey(key(value))
1327 153533f3 Michael Hanselmann
1328 153533f3 Michael Hanselmann
  return sorted(values, key=keyfunc)
1329 a8083063 Iustin Pop
1330 a8083063 Iustin Pop
1331 a8083063 Iustin Pop
def TryConvert(fn, val):
1332 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
1333 a8083063 Iustin Pop

1334 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
1335 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
1336 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
1337 58885d79 Iustin Pop
  exceptions are propagated to the caller.
1338 58885d79 Iustin Pop

1339 58885d79 Iustin Pop
  @type fn: callable
1340 58885d79 Iustin Pop
  @param fn: function to apply to the value
1341 58885d79 Iustin Pop
  @param val: the value to be converted
1342 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
1343 58885d79 Iustin Pop
      otherwise the original value.
1344 a8083063 Iustin Pop

1345 a8083063 Iustin Pop
  """
1346 a8083063 Iustin Pop
  try:
1347 a8083063 Iustin Pop
    nv = fn(val)
1348 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
1349 a8083063 Iustin Pop
    nv = val
1350 a8083063 Iustin Pop
  return nv
1351 a8083063 Iustin Pop
1352 a8083063 Iustin Pop
1353 a8083063 Iustin Pop
def IsValidShellParam(word):
1354 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1355 a8083063 Iustin Pop

1356 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1357 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1358 a8083063 Iustin Pop
  the actual command.
1359 a8083063 Iustin Pop

1360 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1361 a8083063 Iustin Pop
  side.
1362 a8083063 Iustin Pop

1363 58885d79 Iustin Pop
  @type word: str
1364 58885d79 Iustin Pop
  @param word: the word to check
1365 58885d79 Iustin Pop
  @rtype: boolean
1366 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1367 58885d79 Iustin Pop

1368 a8083063 Iustin Pop
  """
1369 0b5303da Iustin Pop
  return bool(_SHELLPARAM_REGEX.match(word))
1370 a8083063 Iustin Pop
1371 a8083063 Iustin Pop
1372 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1373 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1374 a8083063 Iustin Pop

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

1380 58885d79 Iustin Pop
  @type template: str
1381 58885d79 Iustin Pop
  @param template: the string holding the template for the
1382 58885d79 Iustin Pop
      string formatting
1383 58885d79 Iustin Pop
  @rtype: str
1384 58885d79 Iustin Pop
  @return: the expanded command line
1385 58885d79 Iustin Pop

1386 a8083063 Iustin Pop
  """
1387 a8083063 Iustin Pop
  for word in args:
1388 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1389 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1390 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1391 a8083063 Iustin Pop
  return template % args
1392 a8083063 Iustin Pop
1393 a8083063 Iustin Pop
1394 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
1395 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
1396 a8083063 Iustin Pop

1397 58885d79 Iustin Pop
  @type value: int
1398 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
1399 9fbfbb7b Iustin Pop
  @type units: char
1400 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
1401 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
1402 9fbfbb7b Iustin Pop
      - 'm' for MiBs
1403 9fbfbb7b Iustin Pop
      - 'g' for GiBs
1404 9fbfbb7b Iustin Pop
      - 't' for TiBs
1405 58885d79 Iustin Pop
  @rtype: str
1406 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
1407 a8083063 Iustin Pop

1408 a8083063 Iustin Pop
  """
1409 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
1410 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
1411 a8083063 Iustin Pop
1412 9fbfbb7b Iustin Pop
  suffix = ''
1413 9fbfbb7b Iustin Pop
1414 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
1415 9fbfbb7b Iustin Pop
    if units == 'h':
1416 9fbfbb7b Iustin Pop
      suffix = 'M'
1417 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
1418 9fbfbb7b Iustin Pop
1419 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
1420 9fbfbb7b Iustin Pop
    if units == 'h':
1421 9fbfbb7b Iustin Pop
      suffix = 'G'
1422 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
1423 a8083063 Iustin Pop
1424 a8083063 Iustin Pop
  else:
1425 9fbfbb7b Iustin Pop
    if units == 'h':
1426 9fbfbb7b Iustin Pop
      suffix = 'T'
1427 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
1428 a8083063 Iustin Pop
1429 a8083063 Iustin Pop
1430 a8083063 Iustin Pop
def ParseUnit(input_string):
1431 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
1432 a8083063 Iustin Pop

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

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

1478 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
1479 31155d60 Balazs Lecz
  or dash-separated ID ranges
1480 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
1481 31155d60 Balazs Lecz

1482 31155d60 Balazs Lecz
  @type cpu_mask: str
1483 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1484 31155d60 Balazs Lecz
  @rtype: list of int
1485 31155d60 Balazs Lecz
  @return: list of CPU IDs
1486 31155d60 Balazs Lecz

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

1517 3727671e René Nussbaumer
  @type file_obj: str or file handle
1518 3727671e René Nussbaumer
  @param file_obj: path to authorized_keys file
1519 58885d79 Iustin Pop
  @type key: str
1520 58885d79 Iustin Pop
  @param key: string containing key
1521 58885d79 Iustin Pop

1522 a8083063 Iustin Pop
  """
1523 a8083063 Iustin Pop
  key_fields = key.split()
1524 a8083063 Iustin Pop
1525 3727671e René Nussbaumer
  if isinstance(file_obj, basestring):
1526 3727671e René Nussbaumer
    f = open(file_obj, 'a+')
1527 3727671e René Nussbaumer
  else:
1528 3727671e René Nussbaumer
    f = file_obj
1529 3727671e René Nussbaumer
1530 a8083063 Iustin Pop
  try:
1531 a8083063 Iustin Pop
    nl = True
1532 a8083063 Iustin Pop
    for line in f:
1533 a8083063 Iustin Pop
      # Ignore whitespace changes
1534 a8083063 Iustin Pop
      if line.split() == key_fields:
1535 a8083063 Iustin Pop
        break
1536 a8083063 Iustin Pop
      nl = line.endswith('\n')
1537 a8083063 Iustin Pop
    else:
1538 a8083063 Iustin Pop
      if not nl:
1539 a8083063 Iustin Pop
        f.write("\n")
1540 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1541 a8083063 Iustin Pop
      f.write("\n")
1542 a8083063 Iustin Pop
      f.flush()
1543 a8083063 Iustin Pop
  finally:
1544 a8083063 Iustin Pop
    f.close()
1545 a8083063 Iustin Pop
1546 a8083063 Iustin Pop
1547 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1548 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1549 a8083063 Iustin Pop

1550 58885d79 Iustin Pop
  @type file_name: str
1551 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1552 58885d79 Iustin Pop
  @type key: str
1553 58885d79 Iustin Pop
  @param key: string containing key
1554 58885d79 Iustin Pop

1555 a8083063 Iustin Pop
  """
1556 a8083063 Iustin Pop
  key_fields = key.split()
1557 a8083063 Iustin Pop
1558 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1559 a8083063 Iustin Pop
  try:
1560 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1561 a8083063 Iustin Pop
    try:
1562 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1563 59f82e3f Michael Hanselmann
      try:
1564 59f82e3f Michael Hanselmann
        for line in f:
1565 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1566 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1567 59f82e3f Michael Hanselmann
            out.write(line)
1568 899d2a81 Michael Hanselmann
1569 899d2a81 Michael Hanselmann
        out.flush()
1570 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1571 899d2a81 Michael Hanselmann
      finally:
1572 899d2a81 Michael Hanselmann
        f.close()
1573 899d2a81 Michael Hanselmann
    finally:
1574 899d2a81 Michael Hanselmann
      out.close()
1575 899d2a81 Michael Hanselmann
  except:
1576 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1577 899d2a81 Michael Hanselmann
    raise
1578 899d2a81 Michael Hanselmann
1579 899d2a81 Michael Hanselmann
1580 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1581 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1582 899d2a81 Michael Hanselmann

1583 58885d79 Iustin Pop
  @type file_name: str
1584 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1585 58885d79 Iustin Pop
  @type ip: str
1586 58885d79 Iustin Pop
  @param ip: the IP address
1587 58885d79 Iustin Pop
  @type hostname: str
1588 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1589 58885d79 Iustin Pop
  @type aliases: list
1590 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1591 58885d79 Iustin Pop

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

1621 58885d79 Iustin Pop
  @type hostname: str
1622 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1623 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1624 ea8ac9c9 René Nussbaumer
  @type ip: str
1625 ea8ac9c9 René Nussbaumer
  @param ip: The ip address of the host
1626 58885d79 Iustin Pop

1627 d9c02ca6 Michael Hanselmann
  """
1628 ea8ac9c9 René Nussbaumer
  SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]])
1629 d9c02ca6 Michael Hanselmann
1630 d9c02ca6 Michael Hanselmann
1631 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1632 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1633 899d2a81 Michael Hanselmann

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

1636 58885d79 Iustin Pop
  @type file_name: str
1637 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1638 58885d79 Iustin Pop
  @type hostname: str
1639 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1640 58885d79 Iustin Pop

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

1670 58885d79 Iustin Pop
  @type hostname: str
1671 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1672 58885d79 Iustin Pop
      full and shot name will be removed from
1673 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1674 58885d79 Iustin Pop

1675 d9c02ca6 Michael Hanselmann
  """
1676 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname)
1677 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0])
1678 d9c02ca6 Michael Hanselmann
1679 d9c02ca6 Michael Hanselmann
1680 1d466a4f Michael Hanselmann
def TimestampForFilename():
1681 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1682 1d466a4f Michael Hanselmann

1683 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1684 1d466a4f Michael Hanselmann
  separators.
1685 1d466a4f Michael Hanselmann

1686 1d466a4f Michael Hanselmann
  """
1687 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1688 1d466a4f Michael Hanselmann
1689 1d466a4f Michael Hanselmann
1690 a8083063 Iustin Pop
def CreateBackup(file_name):
1691 a8083063 Iustin Pop
  """Creates a backup of a file.
1692 a8083063 Iustin Pop

1693 58885d79 Iustin Pop
  @type file_name: str
1694 58885d79 Iustin Pop
  @param file_name: file to be backed up
1695 58885d79 Iustin Pop
  @rtype: str
1696 58885d79 Iustin Pop
  @return: the path to the newly created backup
1697 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1698 a8083063 Iustin Pop

1699 a8083063 Iustin Pop
  """
1700 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1701 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1702 3ecf6786 Iustin Pop
                                file_name)
1703 a8083063 Iustin Pop
1704 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1705 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1706 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1707 081b1e69 Michael Hanselmann
1708 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1709 081b1e69 Michael Hanselmann
  try:
1710 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1711 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1712 081b1e69 Michael Hanselmann
    try:
1713 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1714 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1715 081b1e69 Michael Hanselmann
    finally:
1716 081b1e69 Michael Hanselmann
      fdst.close()
1717 081b1e69 Michael Hanselmann
  finally:
1718 081b1e69 Michael Hanselmann
    fsrc.close()
1719 081b1e69 Michael Hanselmann
1720 a8083063 Iustin Pop
  return backup_name
1721 a8083063 Iustin Pop
1722 a8083063 Iustin Pop
1723 a8083063 Iustin Pop
def ShellQuote(value):
1724 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1725 3ecf6786 Iustin Pop

1726 58885d79 Iustin Pop
  @type value: str
1727 58885d79 Iustin Pop
  @param value: the argument to be quoted
1728 58885d79 Iustin Pop
  @rtype: str
1729 58885d79 Iustin Pop
  @return: the quoted value
1730 58885d79 Iustin Pop

1731 a8083063 Iustin Pop
  """
1732 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1733 a8083063 Iustin Pop
    return value
1734 a8083063 Iustin Pop
  else:
1735 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1736 a8083063 Iustin Pop
1737 a8083063 Iustin Pop
1738 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1739 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1740 58885d79 Iustin Pop

1741 58885d79 Iustin Pop
  @type args: list
1742 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1743 58885d79 Iustin Pop
  @rtype: str
1744 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1745 a8083063 Iustin Pop

1746 a8083063 Iustin Pop
  """
1747 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1748 88d14415 Michael Hanselmann
1749 88d14415 Michael Hanselmann
1750 858905fb Michael Hanselmann
class ShellWriter:
1751 858905fb Michael Hanselmann
  """Helper class to write scripts with indentation.
1752 858905fb Michael Hanselmann

1753 858905fb Michael Hanselmann
  """
1754 858905fb Michael Hanselmann
  INDENT_STR = "  "
1755 858905fb Michael Hanselmann
1756 858905fb Michael Hanselmann
  def __init__(self, fh):
1757 858905fb Michael Hanselmann
    """Initializes this class.
1758 858905fb Michael Hanselmann

1759 858905fb Michael Hanselmann
    """
1760 858905fb Michael Hanselmann
    self._fh = fh
1761 858905fb Michael Hanselmann
    self._indent = 0
1762 858905fb Michael Hanselmann
1763 858905fb Michael Hanselmann
  def IncIndent(self):
1764 858905fb Michael Hanselmann
    """Increase indentation level by 1.
1765 858905fb Michael Hanselmann

1766 858905fb Michael Hanselmann
    """
1767 858905fb Michael Hanselmann
    self._indent += 1
1768 858905fb Michael Hanselmann
1769 858905fb Michael Hanselmann
  def DecIndent(self):
1770 858905fb Michael Hanselmann
    """Decrease indentation level by 1.
1771 858905fb Michael Hanselmann

1772 858905fb Michael Hanselmann
    """
1773 858905fb Michael Hanselmann
    assert self._indent > 0
1774 858905fb Michael Hanselmann
    self._indent -= 1
1775 858905fb Michael Hanselmann
1776 858905fb Michael Hanselmann
  def Write(self, txt, *args):
1777 858905fb Michael Hanselmann
    """Write line to output file.
1778 858905fb Michael Hanselmann

1779 858905fb Michael Hanselmann
    """
1780 858905fb Michael Hanselmann
    assert self._indent >= 0
1781 858905fb Michael Hanselmann
1782 858905fb Michael Hanselmann
    self._fh.write(self._indent * self.INDENT_STR)
1783 858905fb Michael Hanselmann
1784 858905fb Michael Hanselmann
    if args:
1785 858905fb Michael Hanselmann
      self._fh.write(txt % args)
1786 858905fb Michael Hanselmann
    else:
1787 858905fb Michael Hanselmann
      self._fh.write(txt)
1788 858905fb Michael Hanselmann
1789 858905fb Michael Hanselmann
    self._fh.write("\n")
1790 858905fb Michael Hanselmann
1791 858905fb Michael Hanselmann
1792 b5b8309d Guido Trotter
def ListVisibleFiles(path):
1793 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1794 58885d79 Iustin Pop

1795 58885d79 Iustin Pop
  @type path: str
1796 58885d79 Iustin Pop
  @param path: the directory to enumerate
1797 58885d79 Iustin Pop
  @rtype: list
1798 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1799 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1800 eedbda4b Michael Hanselmann

1801 eedbda4b Michael Hanselmann
  """
1802 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1803 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1804 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1805 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1806 f3299a07 Michael Hanselmann
  return files
1807 2f8b60b3 Iustin Pop
1808 2f8b60b3 Iustin Pop
1809 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1810 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1811 257f4c0a Iustin Pop

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

1816 2f8b60b3 Iustin Pop
  """
1817 2f8b60b3 Iustin Pop
  try:
1818 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1819 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1820 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1821 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1822 257f4c0a Iustin Pop
    else:
1823 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1824 257f4c0a Iustin Pop
                                   type(user))
1825 2f8b60b3 Iustin Pop
  except KeyError:
1826 2f8b60b3 Iustin Pop
    return default
1827 2f8b60b3 Iustin Pop
  return result.pw_dir
1828 59072e7e Michael Hanselmann
1829 59072e7e Michael Hanselmann
1830 24818e8f Michael Hanselmann
def NewUUID():
1831 59072e7e Michael Hanselmann
  """Returns a random UUID.
1832 59072e7e Michael Hanselmann

1833 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1834 58885d79 Iustin Pop
      filesystem.
1835 58885d79 Iustin Pop
  @rtype: str
1836 58885d79 Iustin Pop

1837 59072e7e Michael Hanselmann
  """
1838 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1839 087b34fe Iustin Pop
1840 087b34fe Iustin Pop
1841 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1842 33081d90 Iustin Pop
  """Generates a random secret.
1843 33081d90 Iustin Pop

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

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

1852 33081d90 Iustin Pop
  """
1853 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1854 33081d90 Iustin Pop
1855 33081d90 Iustin Pop
1856 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1857 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1858 9dae41ad Guido Trotter

1859 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1860 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1861 9dae41ad Guido Trotter

1862 9dae41ad Guido Trotter
  """
1863 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1864 9dae41ad Guido Trotter
    try:
1865 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1866 9dae41ad Guido Trotter
    except EnvironmentError, err:
1867 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1868 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1869 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1870 b73360e3 Balazs Lecz
    try:
1871 b73360e3 Balazs Lecz
      os.chmod(dir_name, dir_mode)
1872 b73360e3 Balazs Lecz
    except EnvironmentError, err:
1873 b73360e3 Balazs Lecz
      raise errors.GenericError("Cannot change directory permissions on"
1874 b73360e3 Balazs Lecz
                                " '%s': %s" % (dir_name, err))
1875 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1876 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1877 9dae41ad Guido Trotter
1878 9dae41ad Guido Trotter
1879 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1880 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1881 ca0aa6d0 Michael Hanselmann

1882 016308cb Iustin Pop
  @type size: int
1883 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1884 58885d79 Iustin Pop
  @rtype: str
1885 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1886 ca0aa6d0 Michael Hanselmann

1887 ca0aa6d0 Michael Hanselmann
  """
1888 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1889 ca0aa6d0 Michael Hanselmann
  try:
1890 582ed043 Guido Trotter
    return f.read(size)
1891 ca0aa6d0 Michael Hanselmann
  finally:
1892 ca0aa6d0 Michael Hanselmann
    f.close()
1893 ca0aa6d0 Michael Hanselmann
1894 ca0aa6d0 Michael Hanselmann
1895 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1896 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1897 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1898 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1899 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1900 087b34fe Iustin Pop
  """(Over)write a file atomically.
1901 087b34fe Iustin Pop

1902 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1903 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1904 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1905 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1906 087b34fe Iustin Pop
  mtime/atime of the file.
1907 087b34fe Iustin Pop

1908 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1909 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1910 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1911 087b34fe Iustin Pop
  temporary file should be removed.
1912 087b34fe Iustin Pop

1913 58885d79 Iustin Pop
  @type file_name: str
1914 58885d79 Iustin Pop
  @param file_name: the target filename
1915 58885d79 Iustin Pop
  @type fn: callable
1916 58885d79 Iustin Pop
  @param fn: content writing function, called with
1917 58885d79 Iustin Pop
      file descriptor as parameter
1918 69efe319 Michael Hanselmann
  @type data: str
1919 58885d79 Iustin Pop
  @param data: contents of the file
1920 58885d79 Iustin Pop
  @type mode: int
1921 58885d79 Iustin Pop
  @param mode: file mode
1922 58885d79 Iustin Pop
  @type uid: int
1923 58885d79 Iustin Pop
  @param uid: the owner of the file
1924 58885d79 Iustin Pop
  @type gid: int
1925 58885d79 Iustin Pop
  @param gid: the group of the file
1926 58885d79 Iustin Pop
  @type atime: int
1927 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1928 58885d79 Iustin Pop
  @type mtime: int
1929 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1930 58885d79 Iustin Pop
  @type close: boolean
1931 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1932 58885d79 Iustin Pop
  @type prewrite: callable
1933 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1934 58885d79 Iustin Pop
  @type postwrite: callable
1935 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1936 58885d79 Iustin Pop

1937 58885d79 Iustin Pop
  @rtype: None or int
1938 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1939 58885d79 Iustin Pop
      otherwise the file descriptor
1940 58885d79 Iustin Pop

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

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

1997 9e100285 Iustin Pop
  Either the path to the file or the fd must be given.
1998 9e100285 Iustin Pop

1999 9e100285 Iustin Pop
  @param path: the file path
2000 9e100285 Iustin Pop
  @param fd: a file descriptor
2001 9e100285 Iustin Pop
  @return: a tuple of (device number, inode number, mtime)
2002 9e100285 Iustin Pop

2003 9e100285 Iustin Pop
  """
2004 9e100285 Iustin Pop
  if [path, fd].count(None) != 1:
2005 9e100285 Iustin Pop
    raise errors.ProgrammerError("One and only one of fd/path must be given")
2006 9e100285 Iustin Pop
2007 9e100285 Iustin Pop
  if fd is None:
2008 9e100285 Iustin Pop
    st = os.stat(path)
2009 9e100285 Iustin Pop
  else:
2010 9e100285 Iustin Pop
    st = os.fstat(fd)
2011 9e100285 Iustin Pop
2012 9e100285 Iustin Pop
  return (st.st_dev, st.st_ino, st.st_mtime)
2013 9e100285 Iustin Pop
2014 9e100285 Iustin Pop
2015 9e100285 Iustin Pop
def VerifyFileID(fi_disk, fi_ours):
2016 9e100285 Iustin Pop
  """Verifies that two file IDs are matching.
2017 9e100285 Iustin Pop

2018 9e100285 Iustin Pop
  Differences in the inode/device are not accepted, but and older
2019 9e100285 Iustin Pop
  timestamp for fi_disk is accepted.
2020 9e100285 Iustin Pop

2021 9e100285 Iustin Pop
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
2022 9e100285 Iustin Pop
      file data
2023 9e100285 Iustin Pop
  @param fi_ours: tuple (dev, inode, mtime) representing the last
2024 9e100285 Iustin Pop
      written file data
2025 9e100285 Iustin Pop
  @rtype: boolean
2026 9e100285 Iustin Pop

2027 9e100285 Iustin Pop
  """
2028 9e100285 Iustin Pop
  (d1, i1, m1) = fi_disk
2029 9e100285 Iustin Pop
  (d2, i2, m2) = fi_ours
2030 9e100285 Iustin Pop
2031 9e100285 Iustin Pop
  return (d1, i1) == (d2, i2) and m1 <= m2
2032 9e100285 Iustin Pop
2033 9e100285 Iustin Pop
2034 4138d39f Iustin Pop
def SafeWriteFile(file_name, file_id, **kwargs):
2035 4138d39f Iustin Pop
  """Wraper over L{WriteFile} that locks the target file.
2036 4138d39f Iustin Pop

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

2040 4138d39f Iustin Pop
  @type file_name: str
2041 4138d39f Iustin Pop
  @param file_name: the target filename
2042 4138d39f Iustin Pop
  @type file_id: tuple
2043 4138d39f Iustin Pop
  @param file_id: a result from L{GetFileID}
2044 4138d39f Iustin Pop

2045 4138d39f Iustin Pop
  """
2046 4138d39f Iustin Pop
  fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
2047 4138d39f Iustin Pop
  try:
2048 4138d39f Iustin Pop
    LockFile(fd)
2049 4138d39f Iustin Pop
    if file_id is not None:
2050 4138d39f Iustin Pop
      disk_id = GetFileID(fd=fd)
2051 4138d39f Iustin Pop
      if not VerifyFileID(disk_id, file_id):
2052 4138d39f Iustin Pop
        raise errors.LockError("Cannot overwrite file %s, it has been modified"
2053 4138d39f Iustin Pop
                               " since last written" % file_name)
2054 4138d39f Iustin Pop
    return WriteFile(file_name, **kwargs)
2055 4138d39f Iustin Pop
  finally:
2056 4138d39f Iustin Pop
    os.close(fd)
2057 4138d39f Iustin Pop
2058 4138d39f Iustin Pop
2059 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
2060 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
2061 e587b46a Guido Trotter

2062 e587b46a Guido Trotter
  @type strict: boolean
2063 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
2064 e587b46a Guido Trotter
      non-empty line
2065 e587b46a Guido Trotter

2066 e587b46a Guido Trotter
  """
2067 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
2068 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
2069 e587b46a Guido Trotter
  if not file_lines or not full_lines:
2070 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
2071 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
2072 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
2073 e587b46a Guido Trotter
                              file_name)
2074 e587b46a Guido Trotter
  return full_lines[0]
2075 e587b46a Guido Trotter
2076 e587b46a Guido Trotter
2077 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
2078 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
2079 7b4126b7 Iustin Pop

2080 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
2081 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
2082 7b4126b7 Iustin Pop
  value, the index will be returned.
2083 7b4126b7 Iustin Pop

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

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

2089 58885d79 Iustin Pop
  @type seq: sequence
2090 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
2091 58885d79 Iustin Pop
  @type base: int
2092 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
2093 58885d79 Iustin Pop
  @rtype: int
2094 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
2095 7b4126b7 Iustin Pop

2096 7b4126b7 Iustin Pop
  """
2097 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
2098 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
2099 7b4126b7 Iustin Pop
    if elem > idx + base:
2100 7b4126b7 Iustin Pop
      # idx is not used
2101 7b4126b7 Iustin Pop
      return idx + base
2102 7b4126b7 Iustin Pop
  return None
2103 7b4126b7 Iustin Pop
2104 7b4126b7 Iustin Pop
2105 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
2106 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
2107 dcd511c8 Guido Trotter

2108 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
2109 dfdc4060 Guido Trotter

2110 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
2111 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
2112 dfdc4060 Guido Trotter
  @type event: integer
2113 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
2114 dcd511c8 Guido Trotter
  @type timeout: float or None
2115 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
2116 dcd511c8 Guido Trotter
  @rtype: int or None
2117 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
2118 dcd511c8 Guido Trotter

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

2147 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
2148 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
2149 dfdc4060 Guido Trotter
  expired.
2150 dfdc4060 Guido Trotter

2151 dfdc4060 Guido Trotter
  """
2152 dfdc4060 Guido Trotter
2153 dfdc4060 Guido Trotter
  def __init__(self, timeout):
2154 dfdc4060 Guido Trotter
    self.timeout = timeout
2155 dfdc4060 Guido Trotter
2156 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
2157 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
2158 dfdc4060 Guido Trotter
    if result is None:
2159 dfdc4060 Guido Trotter
      raise RetryAgain()
2160 dfdc4060 Guido Trotter
    else:
2161 dfdc4060 Guido Trotter
      return result
2162 dfdc4060 Guido Trotter
2163 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
2164 dfdc4060 Guido Trotter
    self.timeout = timeout
2165 dfdc4060 Guido Trotter
2166 dfdc4060 Guido Trotter
2167 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
2168 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
2169 dfdc4060 Guido Trotter

2170 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
2171 dfdc4060 Guido Trotter

2172 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
2173 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
2174 dfdc4060 Guido Trotter
  @type event: integer
2175 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
2176 dfdc4060 Guido Trotter
  @type timeout: float or None
2177 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
2178 dfdc4060 Guido Trotter
  @rtype: int or None
2179 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
2180 dfdc4060 Guido Trotter

2181 dfdc4060 Guido Trotter
  """
2182 dfdc4060 Guido Trotter
  if timeout is not None:
2183 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
2184 1b429e2a Iustin Pop
    try:
2185 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
2186 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
2187 1b429e2a Iustin Pop
    except RetryTimeout:
2188 1b429e2a Iustin Pop
      result = None
2189 dfdc4060 Guido Trotter
  else:
2190 dfdc4060 Guido Trotter
    result = None
2191 dfdc4060 Guido Trotter
    while result is None:
2192 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
2193 dfdc4060 Guido Trotter
  return result
2194 2de64672 Iustin Pop
2195 2de64672 Iustin Pop
2196 f7414041 Michael Hanselmann
def UniqueSequence(seq):
2197 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
2198 f7414041 Michael Hanselmann

2199 f7414041 Michael Hanselmann
  Element order is preserved.
2200 58885d79 Iustin Pop

2201 58885d79 Iustin Pop
  @type seq: sequence
2202 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
2203 58885d79 Iustin Pop
  @rtype: list
2204 58885d79 Iustin Pop
  @return: list of unique elements from seq
2205 58885d79 Iustin Pop

2206 f7414041 Michael Hanselmann
  """
2207 f7414041 Michael Hanselmann
  seen = set()
2208 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
2209 1862d460 Alexander Schreiber
2210 1862d460 Alexander Schreiber
2211 9f5a3645 Michael Hanselmann
def FindDuplicates(seq):
2212 9f5a3645 Michael Hanselmann
  """Identifies duplicates in a list.
2213 9f5a3645 Michael Hanselmann

2214 9f5a3645 Michael Hanselmann
  Does not preserve element order.
2215 9f5a3645 Michael Hanselmann

2216 9f5a3645 Michael Hanselmann
  @type seq: sequence
2217 9f5a3645 Michael Hanselmann
  @param seq: Sequence with source elements
2218 9f5a3645 Michael Hanselmann
  @rtype: list
2219 9f5a3645 Michael Hanselmann
  @return: List of duplicate elements from seq
2220 9f5a3645 Michael Hanselmann

2221 9f5a3645 Michael Hanselmann
  """
2222 9f5a3645 Michael Hanselmann
  dup = set()
2223 9f5a3645 Michael Hanselmann
  seen = set()
2224 9f5a3645 Michael Hanselmann
2225 9f5a3645 Michael Hanselmann
  for item in seq:
2226 9f5a3645 Michael Hanselmann
    if item in seen:
2227 9f5a3645 Michael Hanselmann
      dup.add(item)
2228 9f5a3645 Michael Hanselmann
    else:
2229 9f5a3645 Michael Hanselmann
      seen.add(item)
2230 9f5a3645 Michael Hanselmann
2231 9f5a3645 Michael Hanselmann
  return list(dup)
2232 9f5a3645 Michael Hanselmann
2233 9f5a3645 Michael Hanselmann
2234 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
2235 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
2236 1862d460 Alexander Schreiber

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

2240 58885d79 Iustin Pop
  @type mac: str
2241 58885d79 Iustin Pop
  @param mac: the MAC to be validated
2242 82187135 René Nussbaumer
  @rtype: str
2243 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
2244 82187135 René Nussbaumer

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

2247 1862d460 Alexander Schreiber
  """
2248 8fb00704 Iustin Pop
  if not _MAC_CHECK.match(mac):
2249 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
2250 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
2251 82187135 René Nussbaumer
2252 82187135 René Nussbaumer
  return mac.lower()
2253 06009e27 Iustin Pop
2254 06009e27 Iustin Pop
2255 06009e27 Iustin Pop
def TestDelay(duration):
2256 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
2257 06009e27 Iustin Pop

2258 58885d79 Iustin Pop
  @type duration: float
2259 58885d79 Iustin Pop
  @param duration: the sleep duration
2260 58885d79 Iustin Pop
  @rtype: boolean
2261 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
2262 58885d79 Iustin Pop

2263 06009e27 Iustin Pop
  """
2264 06009e27 Iustin Pop
  if duration < 0:
2265 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
2266 06009e27 Iustin Pop
  time.sleep(duration)
2267 38ea42a1 Iustin Pop
  return True, None
2268 8f765069 Iustin Pop
2269 8f765069 Iustin Pop
2270 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
2271 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
2272 8f765069 Iustin Pop

2273 7d88772a Iustin Pop
  @type fd: int
2274 7d88772a Iustin Pop
  @param fd: the file descriptor
2275 7d88772a Iustin Pop
  @type retries: int
2276 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
2277 7d88772a Iustin Pop
      other error than EBADF
2278 7d88772a Iustin Pop

2279 7d88772a Iustin Pop
  """
2280 7d88772a Iustin Pop
  try:
2281 7d88772a Iustin Pop
    os.close(fd)
2282 7d88772a Iustin Pop
  except OSError, err:
2283 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
2284 7d88772a Iustin Pop
      if retries > 0:
2285 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
2286 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
2287 7d88772a Iustin Pop
    # ignore this and go on
2288 7d88772a Iustin Pop
2289 7d88772a Iustin Pop
2290 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
2291 7d88772a Iustin Pop
  """Close file descriptors.
2292 7d88772a Iustin Pop

2293 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
2294 7d88772a Iustin Pop
  stdin/out/err).
2295 8f765069 Iustin Pop

2296 58885d79 Iustin Pop
  @type noclose_fds: list or None
2297 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
2298 58885d79 Iustin Pop
      that should not be closed
2299 58885d79 Iustin Pop

2300 8f765069 Iustin Pop
  """
2301 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
2302 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
2303 8f765069 Iustin Pop
    try:
2304 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
2305 8f765069 Iustin Pop
      if MAXFD < 0:
2306 8f765069 Iustin Pop
        MAXFD = 1024
2307 8f765069 Iustin Pop
    except OSError:
2308 8f765069 Iustin Pop
      MAXFD = 1024
2309 8f765069 Iustin Pop
  else:
2310 8f765069 Iustin Pop
    MAXFD = 1024
2311 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
2312 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
2313 7d88772a Iustin Pop
    maxfd = MAXFD
2314 7d88772a Iustin Pop
2315 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
2316 7d88772a Iustin Pop
  for fd in range(3, maxfd):
2317 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
2318 7d88772a Iustin Pop
      continue
2319 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2320 7d88772a Iustin Pop
2321 7d88772a Iustin Pop
2322 4c32a8bd Luca Bigliardi
def Mlockall(_ctypes=ctypes):
2323 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
2324 4b6fa0bf Luca Bigliardi

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

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

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

2359 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
2360 7d88772a Iustin Pop
  runs it in the background as a daemon.
2361 7d88772a Iustin Pop

2362 7d88772a Iustin Pop
  @type logfile: str
2363 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2364 7d88772a Iustin Pop
  @rtype: int
2365 5fcc718f Iustin Pop
  @return: the value zero
2366 7d88772a Iustin Pop

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

2408 58885d79 Iustin Pop
  @type name: str
2409 58885d79 Iustin Pop
  @param name: the daemon name
2410 58885d79 Iustin Pop
  @rtype: str
2411 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2412 58885d79 Iustin Pop
      daemon name
2413 b330ac0b Guido Trotter

2414 b330ac0b Guido Trotter
  """
2415 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2416 b330ac0b Guido Trotter
2417 b330ac0b Guido Trotter
2418 2826b361 Guido Trotter
def EnsureDaemon(name):
2419 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2420 2826b361 Guido Trotter

2421 2826b361 Guido Trotter
  """
2422 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2423 2826b361 Guido Trotter
  if result.failed:
2424 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2425 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2426 2826b361 Guido Trotter
    return False
2427 2826b361 Guido Trotter
2428 2826b361 Guido Trotter
  return True
2429 b330ac0b Guido Trotter
2430 b330ac0b Guido Trotter
2431 db147305 Tom Limoncelli
def StopDaemon(name):
2432 db147305 Tom Limoncelli
  """Stop daemon
2433 db147305 Tom Limoncelli

2434 db147305 Tom Limoncelli
  """
2435 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
2436 db147305 Tom Limoncelli
  if result.failed:
2437 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
2438 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
2439 db147305 Tom Limoncelli
    return False
2440 db147305 Tom Limoncelli
2441 db147305 Tom Limoncelli
  return True
2442 db147305 Tom Limoncelli
2443 db147305 Tom Limoncelli
2444 5c4d37f9 Iustin Pop
def WritePidFile(pidfile):
2445 b330ac0b Guido Trotter
  """Write the current process pidfile.
2446 b330ac0b Guido Trotter

2447 614244bd Iustin Pop
  @type pidfile: sting
2448 614244bd Iustin Pop
  @param pidfile: the path to the file to be written
2449 614244bd Iustin Pop
  @raise errors.LockError: if the pid file already exists and
2450 58885d79 Iustin Pop
      points to a live process
2451 614244bd Iustin Pop
  @rtype: int
2452 614244bd Iustin Pop
  @return: the file descriptor of the lock file; do not close this unless
2453 614244bd Iustin Pop
      you want to unlock the pid file
2454 b330ac0b Guido Trotter

2455 b330ac0b Guido Trotter
  """
2456 5c4d37f9 Iustin Pop
  # We don't rename nor truncate the file to not drop locks under
2457 5c4d37f9 Iustin Pop
  # existing processes
2458 5c4d37f9 Iustin Pop
  fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
2459 5c4d37f9 Iustin Pop
2460 5c4d37f9 Iustin Pop
  # Lock the PID file (and fail if not possible to do so). Any code
2461 5c4d37f9 Iustin Pop
  # wanting to send a signal to the daemon should try to lock the PID
2462 5c4d37f9 Iustin Pop
  # file before reading it. If acquiring the lock succeeds, the daemon is
2463 5c4d37f9 Iustin Pop
  # no longer running and the signal should not be sent.
2464 5c4d37f9 Iustin Pop
  LockFile(fd_pidfile)
2465 5c4d37f9 Iustin Pop
2466 5c4d37f9 Iustin Pop
  os.write(fd_pidfile, "%d\n" % os.getpid())
2467 b330ac0b Guido Trotter
2468 5c4d37f9 Iustin Pop
  return fd_pidfile
2469 b330ac0b Guido Trotter
2470 b330ac0b Guido Trotter
2471 b330ac0b Guido Trotter
def RemovePidFile(name):
2472 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2473 b330ac0b Guido Trotter

2474 b330ac0b Guido Trotter
  Any errors are ignored.
2475 b330ac0b Guido Trotter

2476 58885d79 Iustin Pop
  @type name: str
2477 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2478 58885d79 Iustin Pop

2479 b330ac0b Guido Trotter
  """
2480 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2481 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2482 b330ac0b Guido Trotter
  try:
2483 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2484 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2485 b330ac0b Guido Trotter
    pass
2486 b330ac0b Guido Trotter
2487 b330ac0b Guido Trotter
2488 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2489 ff5251bc Iustin Pop
                waitpid=False):
2490 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2491 b2a1f511 Iustin Pop

2492 b2a1f511 Iustin Pop
  @type pid: int
2493 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
2494 38206f3c Iustin Pop
  @type signal_: int
2495 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
2496 b2a1f511 Iustin Pop
  @type timeout: int
2497 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
2498 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
2499 b2a1f511 Iustin Pop
                  will be done
2500 ff5251bc Iustin Pop
  @type waitpid: boolean
2501 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
2502 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
2503 ff5251bc Iustin Pop
      would remain as zombie
2504 b2a1f511 Iustin Pop

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

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

2557 58885d79 Iustin Pop
  @type name: str
2558 58885d79 Iustin Pop
  @param name: the name to look for
2559 58885d79 Iustin Pop
  @type search_path: str
2560 58885d79 Iustin Pop
  @param search_path: location to start at
2561 58885d79 Iustin Pop
  @type test: callable
2562 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2563 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2564 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2565 58885d79 Iustin Pop
  @rtype: str or None
2566 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2567 57c177af Iustin Pop

2568 57c177af Iustin Pop
  """
2569 f95c81bf Iustin Pop
  # validate the filename mask
2570 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
2571 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
2572 f95c81bf Iustin Pop
                     name)
2573 f95c81bf Iustin Pop
    return None
2574 f95c81bf Iustin Pop
2575 57c177af Iustin Pop
  for dir_name in search_path:
2576 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
2577 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
2578 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
2579 f95c81bf Iustin Pop
    # basename
2580 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
2581 57c177af Iustin Pop
      return item_name
2582 57c177af Iustin Pop
  return None
2583 8d1a2a64 Michael Hanselmann
2584 8d1a2a64 Michael Hanselmann
2585 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
2586 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
2587 8d1a2a64 Michael Hanselmann

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

2591 58885d79 Iustin Pop
  @type vglist: dict
2592 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2593 58885d79 Iustin Pop
  @type vgname: str
2594 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2595 58885d79 Iustin Pop
  @type minsize: int
2596 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2597 58885d79 Iustin Pop
  @rtype: None or str
2598 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2599 8d1a2a64 Michael Hanselmann

2600 8d1a2a64 Michael Hanselmann
  """
2601 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2602 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2603 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2604 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2605 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2606 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2607 8d1a2a64 Michael Hanselmann
  return None
2608 7996a135 Iustin Pop
2609 7996a135 Iustin Pop
2610 45bc5e4a Michael Hanselmann
def SplitTime(value):
2611 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2612 739be818 Michael Hanselmann

2613 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2614 45bc5e4a Michael Hanselmann
  @type value: int or float
2615 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2616 739be818 Michael Hanselmann

2617 739be818 Michael Hanselmann
  """
2618 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2619 45bc5e4a Michael Hanselmann
2620 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2621 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2622 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2623 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2624 45bc5e4a Michael Hanselmann
2625 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2626 739be818 Michael Hanselmann
2627 739be818 Michael Hanselmann
2628 739be818 Michael Hanselmann
def MergeTime(timetuple):
2629 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2630 739be818 Michael Hanselmann

2631 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2632 739be818 Michael Hanselmann
  @type timetuple: tuple
2633 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2634 739be818 Michael Hanselmann

2635 739be818 Michael Hanselmann
  """
2636 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2637 739be818 Michael Hanselmann
2638 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2639 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2640 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2641 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2642 739be818 Michael Hanselmann
2643 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2644 739be818 Michael Hanselmann
2645 739be818 Michael Hanselmann
2646 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2647 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2648 de3b8e39 Luca Bigliardi

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

2653 de3b8e39 Luca Bigliardi
  """
2654 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2655 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2656 de3b8e39 Luca Bigliardi

2657 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2658 de3b8e39 Luca Bigliardi

2659 de3b8e39 Luca Bigliardi
    """
2660 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2661 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2662 de3b8e39 Luca Bigliardi
2663 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2664 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2665 de3b8e39 Luca Bigliardi

2666 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2667 de3b8e39 Luca Bigliardi
    /dev/console.
2668 de3b8e39 Luca Bigliardi

2669 de3b8e39 Luca Bigliardi
    """
2670 de3b8e39 Luca Bigliardi
    try:
2671 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2672 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2673 de3b8e39 Luca Bigliardi
      try:
2674 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2675 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2676 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2677 de3b8e39 Luca Bigliardi
        pass
2678 de3b8e39 Luca Bigliardi
2679 de3b8e39 Luca Bigliardi
2680 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2681 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2682 49e60a28 Luca Bigliardi
                 console_logging=False):
2683 82d9caef Iustin Pop
  """Configures the logging module.
2684 82d9caef Iustin Pop

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

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

2776 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2777 da961187 Guido Trotter

2778 da961187 Guido Trotter
  """
2779 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2780 82d9caef Iustin Pop
2781 016d04b3 Michael Hanselmann
2782 4bb678e9 Iustin Pop
def PathJoin(*args):
2783 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2784 4bb678e9 Iustin Pop

2785 4bb678e9 Iustin Pop
  Requirements:
2786 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2787 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2788 4bb678e9 Iustin Pop
        since we check for normalization at the end
2789 4bb678e9 Iustin Pop

2790 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2791 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2792 4bb678e9 Iustin Pop

2793 4bb678e9 Iustin Pop
  """
2794 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2795 4bb678e9 Iustin Pop
  assert args
2796 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2797 4bb678e9 Iustin Pop
  root = args[0]
2798 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2799 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2800 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2801 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2802 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2803 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2804 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2805 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2806 4bb678e9 Iustin Pop
  if prefix != root:
2807 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2808 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2809 4bb678e9 Iustin Pop
  return result
2810 4bb678e9 Iustin Pop
2811 4bb678e9 Iustin Pop
2812 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2813 f65f63ef Iustin Pop
  """Return the last lines from a file.
2814 f65f63ef Iustin Pop

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

2819 f65f63ef Iustin Pop
  @param fname: the file name
2820 f65f63ef Iustin Pop
  @type lines: int
2821 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2822 f65f63ef Iustin Pop

2823 f65f63ef Iustin Pop
  """
2824 f65f63ef Iustin Pop
  fd = open(fname, "r")
2825 f65f63ef Iustin Pop
  try:
2826 f65f63ef Iustin Pop
    fd.seek(0, 2)
2827 f65f63ef Iustin Pop
    pos = fd.tell()
2828 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2829 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2830 f65f63ef Iustin Pop
    raw_data = fd.read()
2831 f65f63ef Iustin Pop
  finally:
2832 f65f63ef Iustin Pop
    fd.close()
2833 f65f63ef Iustin Pop
2834 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2835 f65f63ef Iustin Pop
  return rows[-lines:]
2836 f65f63ef Iustin Pop
2837 f65f63ef Iustin Pop
2838 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2839 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2840 24d70417 Michael Hanselmann

2841 24d70417 Michael Hanselmann
  """
2842 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2843 24d70417 Michael Hanselmann
2844 24d70417 Michael Hanselmann
2845 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2846 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2847 27e46076 Michael Hanselmann

2848 27e46076 Michael Hanselmann
  @type value: string
2849 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2850 27e46076 Michael Hanselmann

2851 27e46076 Michael Hanselmann
  """
2852 f394c0de Iustin Pop
  m = _ASN1_TIME_REGEX.match(value)
2853 27e46076 Michael Hanselmann
  if m:
2854 27e46076 Michael Hanselmann
    # We have an offset
2855 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2856 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2857 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2858 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2859 27e46076 Michael Hanselmann
  else:
2860 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2861 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2862 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2863 27e46076 Michael Hanselmann
    utcoffset = 0
2864 27e46076 Michael Hanselmann
2865 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2866 27e46076 Michael Hanselmann
2867 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2868 27e46076 Michael Hanselmann
2869 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2870 27e46076 Michael Hanselmann
2871 27e46076 Michael Hanselmann
2872 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2873 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2874 27e46076 Michael Hanselmann

2875 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2876 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2877 27e46076 Michael Hanselmann

2878 27e46076 Michael Hanselmann
  """
2879 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2880 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2881 27e46076 Michael Hanselmann
  try:
2882 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2883 27e46076 Michael Hanselmann
  except AttributeError:
2884 27e46076 Michael Hanselmann
    not_before = None
2885 27e46076 Michael Hanselmann
  else:
2886 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2887 27e46076 Michael Hanselmann
2888 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2889 27e46076 Michael Hanselmann
      not_before = None
2890 27e46076 Michael Hanselmann
    else:
2891 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2892 27e46076 Michael Hanselmann
2893 27e46076 Michael Hanselmann
  try:
2894 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2895 27e46076 Michael Hanselmann
  except AttributeError:
2896 27e46076 Michael Hanselmann
    not_after = None
2897 27e46076 Michael Hanselmann
  else:
2898 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2899 27e46076 Michael Hanselmann
2900 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2901 27e46076 Michael Hanselmann
      not_after = None
2902 27e46076 Michael Hanselmann
    else:
2903 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2904 27e46076 Michael Hanselmann
2905 27e46076 Michael Hanselmann
  return (not_before, not_after)
2906 27e46076 Michael Hanselmann
2907 27e46076 Michael Hanselmann
2908 24d70417 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
2909 24d70417 Michael Hanselmann
                            warn_days, error_days):
2910 24d70417 Michael Hanselmann
  """Verifies certificate validity.
2911 24d70417 Michael Hanselmann

2912 24d70417 Michael Hanselmann
  @type expired: bool
2913 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2914 24d70417 Michael Hanselmann
  @type not_before: number or None
2915 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2916 24d70417 Michael Hanselmann
  @type not_after: number or None
2917 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2918 24d70417 Michael Hanselmann
  @type now: number
2919 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2920 24d70417 Michael Hanselmann
  @type warn_days: number or None
2921 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2922 24d70417 Michael Hanselmann
  @type error_days: number or None
2923 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2924 24d70417 Michael Hanselmann

2925 24d70417 Michael Hanselmann
  """
2926 24d70417 Michael Hanselmann
  if expired:
2927 24d70417 Michael Hanselmann
    msg = "Certificate is expired"
2928 24d70417 Michael Hanselmann
2929 24d70417 Michael Hanselmann
    if not_before is not None and not_after is not None:
2930 24d70417 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
2931 24d70417 Michael Hanselmann
              (FormatTimestampWithTZ(not_before),
2932 24d70417 Michael Hanselmann
               FormatTimestampWithTZ(not_after)))
2933 24d70417 Michael Hanselmann
    elif not_before is not None:
2934 24d70417 Michael Hanselmann
      msg += " (valid from %s)" % FormatTimestampWithTZ(not_before)
2935 24d70417 Michael Hanselmann
    elif not_after is not None:
2936 24d70417 Michael Hanselmann
      msg += " (valid until %s)" % FormatTimestampWithTZ(not_after)
2937 24d70417 Michael Hanselmann
2938 24d70417 Michael Hanselmann
    return (CERT_ERROR, msg)
2939 24d70417 Michael Hanselmann
2940 24d70417 Michael Hanselmann
  elif not_before is not None and not_before > now:
2941 24d70417 Michael Hanselmann
    return (CERT_WARNING,
2942 24d70417 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
2943 24d70417 Michael Hanselmann
            FormatTimestampWithTZ(not_before))
2944 24d70417 Michael Hanselmann
2945 24d70417 Michael Hanselmann
  elif not_after is not None:
2946 24d70417 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
2947 24d70417 Michael Hanselmann
2948 24d70417 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
2949 24d70417 Michael Hanselmann
2950 24d70417 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
2951 24d70417 Michael Hanselmann
      return (CERT_ERROR, msg)
2952 24d70417 Michael Hanselmann
2953 24d70417 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
2954 24d70417 Michael Hanselmann
      return (CERT_WARNING, msg)
2955 24d70417 Michael Hanselmann
2956 24d70417 Michael Hanselmann
  return (None, None)
2957 24d70417 Michael Hanselmann
2958 24d70417 Michael Hanselmann
2959 24d70417 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
2960 24d70417 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
2961 24d70417 Michael Hanselmann

2962 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2963 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2964 24d70417 Michael Hanselmann
  @type warn_days: number or None
2965 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2966 24d70417 Michael Hanselmann
  @type error_days: number or None
2967 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2968 24d70417 Michael Hanselmann

2969 24d70417 Michael Hanselmann
  """
2970 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2971 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2972 24d70417 Michael Hanselmann
2973 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2974 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2975 24d70417 Michael Hanselmann
2976 24d70417 Michael Hanselmann
2977 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2978 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2979 68857643 Michael Hanselmann

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

2982 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2983 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2984 68857643 Michael Hanselmann
  @type key: string
2985 68857643 Michael Hanselmann
  @param key: Key for HMAC
2986 68857643 Michael Hanselmann
  @type salt: string
2987 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2988 68857643 Michael Hanselmann
  @rtype: string
2989 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2990 68857643 Michael Hanselmann

2991 68857643 Michael Hanselmann
  """
2992 68857643 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2993 68857643 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
2994 68857643 Michael Hanselmann
2995 68857643 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
2996 68857643 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2997 68857643 Michael Hanselmann
2998 68857643 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
2999 68857643 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
3000 3718bf6d Michael Hanselmann
           Sha1Hmac(key, cert_pem, salt=salt),
3001 68857643 Michael Hanselmann
           cert_pem))
3002 68857643 Michael Hanselmann
3003 68857643 Michael Hanselmann
3004 68857643 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
3005 68857643 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
3006 68857643 Michael Hanselmann

3007 68857643 Michael Hanselmann
  """
3008 68857643 Michael Hanselmann
  # Extract signature from original PEM data
3009 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
3010 68857643 Michael Hanselmann
    if line.startswith("---"):
3011 68857643 Michael Hanselmann
      break
3012 68857643 Michael Hanselmann
3013 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
3014 68857643 Michael Hanselmann
    if m:
3015 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
3016 68857643 Michael Hanselmann
3017 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
3018 68857643 Michael Hanselmann
3019 68857643 Michael Hanselmann
3020 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
3021 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
3022 68857643 Michael Hanselmann

3023 68857643 Michael Hanselmann
  @type cert_pem: string
3024 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
3025 68857643 Michael Hanselmann
  @type key: string
3026 68857643 Michael Hanselmann
  @param key: Key for HMAC
3027 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
3028 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
3029 68857643 Michael Hanselmann

3030 68857643 Michael Hanselmann
  """
3031 68857643 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
3032 68857643 Michael Hanselmann
3033 68857643 Michael Hanselmann
  # Load certificate
3034 68857643 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
3035 68857643 Michael Hanselmann
3036 68857643 Michael Hanselmann
  # Dump again to ensure it's in a sane format
3037 68857643 Michael Hanselmann
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3038 68857643 Michael Hanselmann
3039 3718bf6d Michael Hanselmann
  if not VerifySha1Hmac(key, sane_pem, signature, salt=salt):
3040 68857643 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
3041 68857643 Michael Hanselmann
3042 68857643 Michael Hanselmann
  return (cert, salt)
3043 68857643 Michael Hanselmann
3044 68857643 Michael Hanselmann
3045 3718bf6d Michael Hanselmann
def Sha1Hmac(key, text, salt=None):
3046 615aaaba Michael Hanselmann
  """Calculates the HMAC-SHA1 digest of a text.
3047 615aaaba Michael Hanselmann

3048 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
3049 615aaaba Michael Hanselmann

3050 615aaaba Michael Hanselmann
  @type key: string
3051 615aaaba Michael Hanselmann
  @param key: Secret key
3052 615aaaba Michael Hanselmann
  @type text: string
3053 615aaaba Michael Hanselmann

3054 615aaaba Michael Hanselmann
  """
3055 3718bf6d Michael Hanselmann
  if salt:
3056 3718bf6d Michael Hanselmann
    salted_text = salt + text
3057 3718bf6d Michael Hanselmann
  else:
3058 3718bf6d Michael Hanselmann
    salted_text = text
3059 3718bf6d Michael Hanselmann
3060 716a32cb Guido Trotter
  return hmac.new(key, salted_text, compat.sha1).hexdigest()
3061 615aaaba Michael Hanselmann
3062 615aaaba Michael Hanselmann
3063 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
3064 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
3065 615aaaba Michael Hanselmann

3066 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
3067 615aaaba Michael Hanselmann

3068 615aaaba Michael Hanselmann
  @type key: string
3069 615aaaba Michael Hanselmann
  @param key: Secret key
3070 615aaaba Michael Hanselmann
  @type text: string
3071 615aaaba Michael Hanselmann
  @type digest: string
3072 615aaaba Michael Hanselmann
  @param digest: Expected digest
3073 615aaaba Michael Hanselmann
  @rtype: bool
3074 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
3075 615aaaba Michael Hanselmann

3076 615aaaba Michael Hanselmann
  """
3077 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
3078 615aaaba Michael Hanselmann
3079 615aaaba Michael Hanselmann
3080 26f15862 Iustin Pop
def SafeEncode(text):
3081 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
3082 26f15862 Iustin Pop

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

3092 26f15862 Iustin Pop
  @type text: str or unicode
3093 26f15862 Iustin Pop
  @param text: input data
3094 26f15862 Iustin Pop
  @rtype: str
3095 26f15862 Iustin Pop
  @return: a safe version of text
3096 26f15862 Iustin Pop

3097 26f15862 Iustin Pop
  """
3098 d392fa34 Iustin Pop
  if isinstance(text, unicode):
3099 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
3100 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
3101 d392fa34 Iustin Pop
  resu = ""
3102 d392fa34 Iustin Pop
  for char in text:
3103 d392fa34 Iustin Pop
    c = ord(char)
3104 d392fa34 Iustin Pop
    if char  == '\t':
3105 d392fa34 Iustin Pop
      resu += r'\t'
3106 d392fa34 Iustin Pop
    elif char == '\n':
3107 d392fa34 Iustin Pop
      resu += r'\n'
3108 d392fa34 Iustin Pop
    elif char == '\r':
3109 d392fa34 Iustin Pop
      resu += r'\'r'
3110 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
3111 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
3112 d392fa34 Iustin Pop
    else:
3113 d392fa34 Iustin Pop
      resu += char
3114 d392fa34 Iustin Pop
  return resu
3115 26f15862 Iustin Pop
3116 26f15862 Iustin Pop
3117 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
3118 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
3119 5b69bc7c Iustin Pop

3120 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
3121 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
3122 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
3123 5b69bc7c Iustin Pop
  separator):
3124 5b69bc7c Iustin Pop
    - a plain , separates the elements
3125 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
3126 5b69bc7c Iustin Pop
      backslash plus a separator comma
3127 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
3128 5b69bc7c Iustin Pop
      non-separator comma
3129 5b69bc7c Iustin Pop

3130 5b69bc7c Iustin Pop
  @type text: string
3131 5b69bc7c Iustin Pop
  @param text: the string to split
3132 5b69bc7c Iustin Pop
  @type sep: string
3133 5b69bc7c Iustin Pop
  @param text: the separator
3134 5b69bc7c Iustin Pop
  @rtype: string
3135 5b69bc7c Iustin Pop
  @return: a list of strings
3136 5b69bc7c Iustin Pop

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

3162 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
3163 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
3164 ab3e6da8 Iustin Pop

3165 ab3e6da8 Iustin Pop
  """
3166 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
3167 ab3e6da8 Iustin Pop
3168 ab3e6da8 Iustin Pop
3169 691c81b7 Michael Hanselmann
def FindMatch(data, name):
3170 691c81b7 Michael Hanselmann
  """Tries to find an item in a dictionary matching a name.
3171 691c81b7 Michael Hanselmann

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

3176 691c81b7 Michael Hanselmann
  @type data: dict
3177 691c81b7 Michael Hanselmann
  @param data: Dictionary containing data
3178 691c81b7 Michael Hanselmann
  @type name: string
3179 691c81b7 Michael Hanselmann
  @param name: Name to look for
3180 691c81b7 Michael Hanselmann
  @rtype: tuple; (value in dictionary, matched groups as list)
3181 691c81b7 Michael Hanselmann

3182 691c81b7 Michael Hanselmann
  """
3183 691c81b7 Michael Hanselmann
  if name in data:
3184 691c81b7 Michael Hanselmann
    return (data[name], [])
3185 691c81b7 Michael Hanselmann
3186 691c81b7 Michael Hanselmann
  for key, value in data.items():
3187 691c81b7 Michael Hanselmann
    # Regex objects
3188 691c81b7 Michael Hanselmann
    if hasattr(key, "match"):
3189 691c81b7 Michael Hanselmann
      m = key.match(name)
3190 691c81b7 Michael Hanselmann
      if m:
3191 691c81b7 Michael Hanselmann
        return (value, list(m.groups()))
3192 691c81b7 Michael Hanselmann
3193 691c81b7 Michael Hanselmann
  return None
3194 691c81b7 Michael Hanselmann
3195 691c81b7 Michael Hanselmann
3196 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
3197 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
3198 3f6a47a8 Michael Hanselmann

3199 3f6a47a8 Michael Hanselmann
  @type value: int
3200 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
3201 3f6a47a8 Michael Hanselmann
  @rtype: int
3202 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
3203 3f6a47a8 Michael Hanselmann

3204 3f6a47a8 Michael Hanselmann
  """
3205 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
3206 3f6a47a8 Michael Hanselmann
3207 3f6a47a8 Michael Hanselmann
3208 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
3209 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
3210 3f6a47a8 Michael Hanselmann

3211 3f6a47a8 Michael Hanselmann
  @type path: string
3212 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
3213 3f6a47a8 Michael Hanselmann
  @rtype: int
3214 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
3215 3f6a47a8 Michael Hanselmann

3216 3f6a47a8 Michael Hanselmann
  """
3217 3f6a47a8 Michael Hanselmann
  size = 0
3218 3f6a47a8 Michael Hanselmann
3219 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
3220 2a887df9 Michael Hanselmann
    for filename in files:
3221 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
3222 3f6a47a8 Michael Hanselmann
      size += st.st_size
3223 3f6a47a8 Michael Hanselmann
3224 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
3225 3f6a47a8 Michael Hanselmann
3226 3f6a47a8 Michael Hanselmann
3227 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
3228 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
3229 1b045f5d Balazs Lecz

3230 1b045f5d Balazs Lecz
  This function is Linux-specific.
3231 1b045f5d Balazs Lecz

3232 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
3233 1b045f5d Balazs Lecz
  @rtype: list of tuples
3234 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
3235 1b045f5d Balazs Lecz

3236 1b045f5d Balazs Lecz
  """
3237 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
3238 1b045f5d Balazs Lecz
  data = []
3239 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
3240 1b045f5d Balazs Lecz
  for line in mountlines:
3241 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
3242 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
3243 1b045f5d Balazs Lecz
3244 1b045f5d Balazs Lecz
  return data
3245 1b045f5d Balazs Lecz
3246 1b045f5d Balazs Lecz
3247 620a85fd Iustin Pop
def GetFilesystemStats(path):
3248 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
3249 3f6a47a8 Michael Hanselmann

3250 3f6a47a8 Michael Hanselmann
  @type path: string
3251 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
3252 3f6a47a8 Michael Hanselmann
  @rtype: int
3253 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
3254 3f6a47a8 Michael Hanselmann

3255 3f6a47a8 Michael Hanselmann
  """
3256 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
3257 3f6a47a8 Michael Hanselmann
3258 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
3259 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
3260 620a85fd Iustin Pop
  return (tsize, fsize)
3261 3f6a47a8 Michael Hanselmann
3262 3f6a47a8 Michael Hanselmann
3263 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
3264 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
3265 eb58f7bd Michael Hanselmann

3266 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
3267 eb58f7bd Michael Hanselmann

3268 eb58f7bd Michael Hanselmann
  @type fn: callable
3269 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
3270 bdefe5dd Michael Hanselmann
  @rtype: bool
3271 bdefe5dd Michael Hanselmann
  @return: Function's result
3272 eb58f7bd Michael Hanselmann

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

3313 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
3314 560cbec1 Michael Hanselmann

3315 560cbec1 Michael Hanselmann
  @rtype: bool
3316 560cbec1 Michael Hanselmann
  @return: Whether process was found
3317 560cbec1 Michael Hanselmann

3318 560cbec1 Michael Hanselmann
  """
3319 560cbec1 Michael Hanselmann
  try:
3320 560cbec1 Michael Hanselmann
    fn(*args, **kwargs)
3321 560cbec1 Michael Hanselmann
  except EnvironmentError, err:
3322 560cbec1 Michael Hanselmann
    # Ignore ESRCH
3323 560cbec1 Michael Hanselmann
    if err.errno == errno.ESRCH:
3324 560cbec1 Michael Hanselmann
      return False
3325 560cbec1 Michael Hanselmann
    raise
3326 560cbec1 Michael Hanselmann
3327 560cbec1 Michael Hanselmann
  return True
3328 560cbec1 Michael Hanselmann
3329 560cbec1 Michael Hanselmann
3330 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
3331 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
3332 232144d0 Guido Trotter

3333 232144d0 Guido Trotter
  """
3334 232144d0 Guido Trotter
  try:
3335 232144d0 Guido Trotter
    return fn(*args, **kwargs)
3336 965d0e5b Guido Trotter
  except EnvironmentError, err:
3337 2fd7f564 Guido Trotter
    if err.errno == errno.EINTR:
3338 2fd7f564 Guido Trotter
      return None
3339 2fd7f564 Guido Trotter
    else:
3340 232144d0 Guido Trotter
      raise
3341 965d0e5b Guido Trotter
  except (select.error, socket.error), err:
3342 965d0e5b Guido Trotter
    # In python 2.6 and above select.error is an IOError, so it's handled
3343 965d0e5b Guido Trotter
    # above, in 2.5 and below it's not, and it's handled here.
3344 2fd7f564 Guido Trotter
    if err.args and err.args[0] == errno.EINTR:
3345 2fd7f564 Guido Trotter
      return None
3346 2fd7f564 Guido Trotter
    else:
3347 232144d0 Guido Trotter
      raise
3348 232144d0 Guido Trotter
3349 232144d0 Guido Trotter
3350 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
3351 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
3352 eb0f0ce0 Michael Hanselmann

3353 58885d79 Iustin Pop
  @type fd: int
3354 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
3355 58885d79 Iustin Pop

3356 eb0f0ce0 Michael Hanselmann
  """
3357 eb0f0ce0 Michael Hanselmann
  try:
3358 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
3359 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3360 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3361 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3362 eb0f0ce0 Michael Hanselmann
    raise
3363 de499029 Michael Hanselmann
3364 de499029 Michael Hanselmann
3365 3b813dd2 Iustin Pop
def FormatTime(val):
3366 3b813dd2 Iustin Pop
  """Formats a time value.
3367 3b813dd2 Iustin Pop

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

3372 3b813dd2 Iustin Pop
  """
3373 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3374 3b813dd2 Iustin Pop
    return "N/A"
3375 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3376 3b813dd2 Iustin Pop
  # platforms
3377 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3378 3b813dd2 Iustin Pop
3379 3b813dd2 Iustin Pop
3380 f8ea4ada Michael Hanselmann
def FormatSeconds(secs):
3381 f8ea4ada Michael Hanselmann
  """Formats seconds for easier reading.
3382 f8ea4ada Michael Hanselmann

3383 f8ea4ada Michael Hanselmann
  @type secs: number
3384 f8ea4ada Michael Hanselmann
  @param secs: Number of seconds
3385 f8ea4ada Michael Hanselmann
  @rtype: string
3386 f8ea4ada Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
3387 f8ea4ada Michael Hanselmann

3388 f8ea4ada Michael Hanselmann
  """
3389 f8ea4ada Michael Hanselmann
  parts = []
3390 f8ea4ada Michael Hanselmann
3391 f8ea4ada Michael Hanselmann
  secs = round(secs, 0)
3392 f8ea4ada Michael Hanselmann
3393 f8ea4ada Michael Hanselmann
  if secs > 0:
3394 f8ea4ada Michael Hanselmann
    # Negative values would be a bit tricky
3395 f8ea4ada Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
3396 f8ea4ada Michael Hanselmann
      (complete, secs) = divmod(secs, one)
3397 f8ea4ada Michael Hanselmann
      if complete or parts:
3398 f8ea4ada Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
3399 f8ea4ada Michael Hanselmann
3400 f8ea4ada Michael Hanselmann
  parts.append("%ds" % secs)
3401 f8ea4ada Michael Hanselmann
3402 f8ea4ada Michael Hanselmann
  return " ".join(parts)
3403 f8ea4ada Michael Hanselmann
3404 f8ea4ada Michael Hanselmann
3405 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
3406 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
3407 05e50653 Michael Hanselmann

3408 5cbe43a5 Michael Hanselmann
  @type filename: string
3409 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3410 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3411 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3412 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3413 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3414 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3415 5cbe43a5 Michael Hanselmann

3416 05e50653 Michael Hanselmann
  """
3417 05e50653 Michael Hanselmann
  if now is None:
3418 05e50653 Michael Hanselmann
    now = time.time()
3419 05e50653 Michael Hanselmann
3420 05e50653 Michael Hanselmann
  try:
3421 05e50653 Michael Hanselmann
    value = ReadFile(filename)
3422 05e50653 Michael Hanselmann
  except IOError, err:
3423 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
3424 05e50653 Michael Hanselmann
      raise
3425 05e50653 Michael Hanselmann
    value = None
3426 05e50653 Michael Hanselmann
3427 05e50653 Michael Hanselmann
  if value is not None:
3428 05e50653 Michael Hanselmann
    try:
3429 05e50653 Michael Hanselmann
      value = int(value)
3430 05e50653 Michael Hanselmann
    except ValueError:
3431 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
3432 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
3433 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
3434 05e50653 Michael Hanselmann
      value = None
3435 05e50653 Michael Hanselmann
3436 05e50653 Michael Hanselmann
    if value is not None:
3437 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
3438 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
3439 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
3440 5cbe43a5 Michael Hanselmann
        value = None
3441 5cbe43a5 Michael Hanselmann
3442 5cbe43a5 Michael Hanselmann
      elif now > value:
3443 05e50653 Michael Hanselmann
        value = None
3444 05e50653 Michael Hanselmann
3445 05e50653 Michael Hanselmann
  return value
3446 05e50653 Michael Hanselmann
3447 05e50653 Michael Hanselmann
3448 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
3449 de0ea66b Michael Hanselmann
  """Retry loop timed out.
3450 de0ea66b Michael Hanselmann

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

3455 de0ea66b Michael Hanselmann
  """
3456 506be7c5 Guido Trotter
  def RaiseInner(self):
3457 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3458 506be7c5 Guido Trotter
      raise self.args[0]
3459 506be7c5 Guido Trotter
    else:
3460 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3461 de0ea66b Michael Hanselmann
3462 de0ea66b Michael Hanselmann
3463 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3464 de0ea66b Michael Hanselmann
  """Retry again.
3465 de0ea66b Michael Hanselmann

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

3470 de0ea66b Michael Hanselmann
  """
3471 de0ea66b Michael Hanselmann
3472 de0ea66b Michael Hanselmann
3473 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3474 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3475 de0ea66b Michael Hanselmann

3476 de0ea66b Michael Hanselmann
  """
3477 de0ea66b Michael Hanselmann
  __slots__ = [
3478 de0ea66b Michael Hanselmann
    "_factor",
3479 de0ea66b Michael Hanselmann
    "_limit",
3480 de0ea66b Michael Hanselmann
    "_next",
3481 de0ea66b Michael Hanselmann
    "_start",
3482 de0ea66b Michael Hanselmann
    ]
3483 de0ea66b Michael Hanselmann
3484 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3485 de0ea66b Michael Hanselmann
    """Initializes this class.
3486 de0ea66b Michael Hanselmann

3487 de0ea66b Michael Hanselmann
    @type start: float
3488 de0ea66b Michael Hanselmann
    @param start: Initial delay
3489 de0ea66b Michael Hanselmann
    @type factor: float
3490 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3491 de0ea66b Michael Hanselmann
    @type limit: float or None
3492 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3493 de0ea66b Michael Hanselmann

3494 de0ea66b Michael Hanselmann
    """
3495 de0ea66b Michael Hanselmann
    assert start > 0.0
3496 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3497 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3498 de0ea66b Michael Hanselmann
3499 de0ea66b Michael Hanselmann
    self._start = start
3500 de0ea66b Michael Hanselmann
    self._factor = factor
3501 de0ea66b Michael Hanselmann
    self._limit = limit
3502 de0ea66b Michael Hanselmann
3503 de0ea66b Michael Hanselmann
    self._next = start
3504 de0ea66b Michael Hanselmann
3505 de0ea66b Michael Hanselmann
  def __call__(self):
3506 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3507 de0ea66b Michael Hanselmann

3508 de0ea66b Michael Hanselmann
    """
3509 de0ea66b Michael Hanselmann
    current = self._next
3510 de0ea66b Michael Hanselmann
3511 de0ea66b Michael Hanselmann
    # Update for next run
3512 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3513 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3514 de0ea66b Michael Hanselmann
3515 de0ea66b Michael Hanselmann
    return current
3516 de0ea66b Michael Hanselmann
3517 de0ea66b Michael Hanselmann
3518 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3519 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3520 de0ea66b Michael Hanselmann
3521 de0ea66b Michael Hanselmann
3522 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3523 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3524 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3525 de0ea66b Michael Hanselmann

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

3530 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3531 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3532 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3533 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3534 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3535 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3536 de0ea66b Michael Hanselmann

3537 de0ea66b Michael Hanselmann
  @type fn: callable
3538 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3539 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3540 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3541 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3542 de0ea66b Michael Hanselmann
  @type timeout: float
3543 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3544 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3545 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3546 de0ea66b Michael Hanselmann
  @return: Return value of function
3547 de0ea66b Michael Hanselmann

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

3607 bdd5e420 Michael Hanselmann
  """
3608 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3609 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3610 bdd5e420 Michael Hanselmann
  return path
3611 bdd5e420 Michael Hanselmann
3612 bdd5e420 Michael Hanselmann
3613 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3614 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3615 bdd5e420 Michael Hanselmann

3616 bdd5e420 Michael Hanselmann
  @type common_name: string
3617 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3618 a55474c7 Michael Hanselmann
  @type validity: int
3619 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3620 a55474c7 Michael Hanselmann

3621 a55474c7 Michael Hanselmann
  """
3622 bdd5e420 Michael Hanselmann
  # Create private and public key
3623 bdd5e420 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
3624 bdd5e420 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
3625 bdd5e420 Michael Hanselmann
3626 bdd5e420 Michael Hanselmann
  # Create self-signed certificate
3627 bdd5e420 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
3628 bdd5e420 Michael Hanselmann
  if common_name:
3629 bdd5e420 Michael Hanselmann
    cert.get_subject().CN = common_name
3630 bdd5e420 Michael Hanselmann
  cert.set_serial_number(1)
3631 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
3632 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
3633 bdd5e420 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
3634 bdd5e420 Michael Hanselmann
  cert.set_pubkey(key)
3635 bdd5e420 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
3636 bdd5e420 Michael Hanselmann
3637 bdd5e420 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
3638 bdd5e420 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3639 bdd5e420 Michael Hanselmann
3640 bdd5e420 Michael Hanselmann
  return (key_pem, cert_pem)
3641 bdd5e420 Michael Hanselmann
3642 bdd5e420 Michael Hanselmann
3643 600535f0 Manuel Franceschini
def GenerateSelfSignedSslCert(filename, common_name=constants.X509_CERT_CN,
3644 600535f0 Manuel Franceschini
                              validity=constants.X509_CERT_DEFAULT_VALIDITY):
3645 bdd5e420 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
3646 bdd5e420 Michael Hanselmann

3647 2ea65c7d Manuel Franceschini
  @type filename: str
3648 2ea65c7d Manuel Franceschini
  @param filename: path to write certificate to
3649 600535f0 Manuel Franceschini
  @type common_name: string
3650 600535f0 Manuel Franceschini
  @param common_name: commonName value
3651 600535f0 Manuel Franceschini
  @type validity: int
3652 600535f0 Manuel Franceschini
  @param validity: validity of certificate in number of days
3653 600535f0 Manuel Franceschini

3654 bdd5e420 Michael Hanselmann
  """
3655 600535f0 Manuel Franceschini
  # TODO: Investigate using the cluster name instead of X505_CERT_CN for
3656 600535f0 Manuel Franceschini
  # common_name, as cluster-renames are very seldom, and it'd be nice if RAPI
3657 600535f0 Manuel Franceschini
  # and node daemon certificates have the proper Subject/Issuer.
3658 600535f0 Manuel Franceschini
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(common_name,
3659 bdd5e420 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
3660 bdd5e420 Michael Hanselmann
3661 bdd5e420 Michael Hanselmann
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
3662 a55474c7 Michael Hanselmann
3663 a55474c7 Michael Hanselmann
3664 a87b4824 Michael Hanselmann
class FileLock(object):
3665 a87b4824 Michael Hanselmann
  """Utility class for file locks.
3666 a87b4824 Michael Hanselmann

3667 a87b4824 Michael Hanselmann
  """
3668 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3669 58885d79 Iustin Pop
    """Constructor for FileLock.
3670 58885d79 Iustin Pop

3671 b4478d34 Michael Hanselmann
    @type fd: file
3672 b4478d34 Michael Hanselmann
    @param fd: File object
3673 58885d79 Iustin Pop
    @type filename: str
3674 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3675 58885d79 Iustin Pop

3676 58885d79 Iustin Pop
    """
3677 b4478d34 Michael Hanselmann
    self.fd = fd
3678 a87b4824 Michael Hanselmann
    self.filename = filename
3679 b4478d34 Michael Hanselmann
3680 b4478d34 Michael Hanselmann
  @classmethod
3681 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3682 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3683 b4478d34 Michael Hanselmann

3684 b4478d34 Michael Hanselmann
    @type filename: string
3685 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3686 b4478d34 Michael Hanselmann

3687 b4478d34 Michael Hanselmann
    """
3688 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3689 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3690 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3691 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3692 b4478d34 Michael Hanselmann
               filename)
3693 a87b4824 Michael Hanselmann
3694 a87b4824 Michael Hanselmann
  def __del__(self):
3695 a87b4824 Michael Hanselmann
    self.Close()
3696 a87b4824 Michael Hanselmann
3697 a87b4824 Michael Hanselmann
  def Close(self):
3698 58885d79 Iustin Pop
    """Close the file and release the lock.
3699 58885d79 Iustin Pop

3700 58885d79 Iustin Pop
    """
3701 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3702 a87b4824 Michael Hanselmann
      self.fd.close()
3703 a87b4824 Michael Hanselmann
      self.fd = None
3704 a87b4824 Michael Hanselmann
3705 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3706 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3707 aa74b828 Michael Hanselmann

3708 aa74b828 Michael Hanselmann
    @type flag: int
3709 58885d79 Iustin Pop
    @param flag: operation flag
3710 aa74b828 Michael Hanselmann
    @type blocking: bool
3711 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3712 aa74b828 Michael Hanselmann
    @type timeout: None or float
3713 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3714 aa74b828 Michael Hanselmann
                    non-blocking mode).
3715 aa74b828 Michael Hanselmann
    @type errmsg: string
3716 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3717 aa74b828 Michael Hanselmann

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

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

3758 a87b4824 Michael Hanselmann
    """
3759 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3760 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3761 a87b4824 Michael Hanselmann
3762 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3763 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3764 a87b4824 Michael Hanselmann

3765 58885d79 Iustin Pop
    @type blocking: boolean
3766 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3767 58885d79 Iustin Pop
        can lock the file or return immediately
3768 58885d79 Iustin Pop
    @type timeout: int or None
3769 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3770 58885d79 Iustin Pop
        (in blocking mode)
3771 58885d79 Iustin Pop

3772 a87b4824 Michael Hanselmann
    """
3773 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3774 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3775 a87b4824 Michael Hanselmann
3776 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3777 a87b4824 Michael Hanselmann
    """Unlocks the file.
3778 a87b4824 Michael Hanselmann

3779 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3780 58885d79 Iustin Pop
    operation::
3781 58885d79 Iustin Pop

3782 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3783 58885d79 Iustin Pop
      operations.
3784 58885d79 Iustin Pop

3785 58885d79 Iustin Pop
    @type blocking: boolean
3786 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3787 58885d79 Iustin Pop
        can lock the file or return immediately
3788 58885d79 Iustin Pop
    @type timeout: int or None
3789 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3790 58885d79 Iustin Pop
        (in blocking mode)
3791 a87b4824 Michael Hanselmann

3792 a87b4824 Michael Hanselmann
    """
3793 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3794 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3795 a87b4824 Michael Hanselmann
3796 a87b4824 Michael Hanselmann
3797 339be5a8 Michael Hanselmann
class LineSplitter:
3798 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3799 339be5a8 Michael Hanselmann

3800 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3801 339be5a8 Michael Hanselmann

3802 339be5a8 Michael Hanselmann
  """
3803 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3804 339be5a8 Michael Hanselmann
    """Initializes this class.
3805 339be5a8 Michael Hanselmann

3806 339be5a8 Michael Hanselmann
    @type line_fn: callable
3807 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3808 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3809 339be5a8 Michael Hanselmann

3810 339be5a8 Michael Hanselmann
    """
3811 339be5a8 Michael Hanselmann
    assert callable(line_fn)
3812 339be5a8 Michael Hanselmann
3813 339be5a8 Michael Hanselmann
    if args:
3814 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
3815 339be5a8 Michael Hanselmann
      self._line_fn = \
3816 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
3817 339be5a8 Michael Hanselmann
    else:
3818 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
3819 339be5a8 Michael Hanselmann
3820 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
3821 339be5a8 Michael Hanselmann
    self._buffer = ""
3822 339be5a8 Michael Hanselmann
3823 339be5a8 Michael Hanselmann
  def write(self, data):
3824 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
3825 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
3826 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
3827 339be5a8 Michael Hanselmann
3828 339be5a8 Michael Hanselmann
  def flush(self):
3829 339be5a8 Michael Hanselmann
    while self._lines:
3830 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
3831 339be5a8 Michael Hanselmann
3832 339be5a8 Michael Hanselmann
  def close(self):
3833 339be5a8 Michael Hanselmann
    self.flush()
3834 339be5a8 Michael Hanselmann
    if self._buffer:
3835 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
3836 339be5a8 Michael Hanselmann
3837 339be5a8 Michael Hanselmann
3838 451575de Guido Trotter
def SignalHandled(signums):
3839 451575de Guido Trotter
  """Signal Handled decoration.
3840 451575de Guido Trotter

3841 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3842 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3843 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3844 451575de Guido Trotter
  objects as values.
3845 451575de Guido Trotter

3846 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3847 451575de Guido Trotter
  with different handlers.
3848 451575de Guido Trotter

3849 451575de Guido Trotter
  @type signums: list
3850 451575de Guido Trotter
  @param signums: signals to intercept
3851 451575de Guido Trotter

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

3891 b9768937 Michael Hanselmann
    """
3892 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
3893 b9768937 Michael Hanselmann
3894 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
3895 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
3896 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
3897 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
3898 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
3899 b9768937 Michael Hanselmann
3900 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
3901 b9768937 Michael Hanselmann
3902 b9768937 Michael Hanselmann
    # Utility functions
3903 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
3904 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
3905 b9768937 Michael Hanselmann
3906 b9768937 Michael Hanselmann
  def Reset(self):
3907 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
3908 b9768937 Michael Hanselmann

3909 b9768937 Michael Hanselmann
    """
3910 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3911 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3912 b9768937 Michael Hanselmann
      self._previous = None
3913 b9768937 Michael Hanselmann
3914 b9768937 Michael Hanselmann
  def Notify(self):
3915 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3916 b9768937 Michael Hanselmann

3917 b9768937 Michael Hanselmann
    """
3918 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3919 b9768937 Michael Hanselmann
3920 b9768937 Michael Hanselmann
  def __del__(self):
3921 b9768937 Michael Hanselmann
    """Called before object deletion.
3922 b9768937 Michael Hanselmann

3923 b9768937 Michael Hanselmann
    """
3924 b9768937 Michael Hanselmann
    self.Reset()
3925 b9768937 Michael Hanselmann
3926 b9768937 Michael Hanselmann
3927 de499029 Michael Hanselmann
class SignalHandler(object):
3928 de499029 Michael Hanselmann
  """Generic signal handler class.
3929 de499029 Michael Hanselmann

3930 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3931 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3932 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3933 58885d79 Iustin Pop
  signal was sent.
3934 58885d79 Iustin Pop

3935 58885d79 Iustin Pop
  @type signum: list
3936 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3937 58885d79 Iustin Pop
  @type called: boolean
3938 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3939 de499029 Michael Hanselmann

3940 de499029 Michael Hanselmann
  """
3941 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3942 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3943 de499029 Michael Hanselmann

3944 58885d79 Iustin Pop
    @type signum: int or list of ints
3945 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3946 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3947 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3948 de499029 Michael Hanselmann

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

3982 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3983 58885d79 Iustin Pop

3984 de499029 Michael Hanselmann
    """
3985 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3986 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3987 de499029 Michael Hanselmann
      # If successful, remove from dict
3988 de499029 Michael Hanselmann
      del self._previous[signum]
3989 de499029 Michael Hanselmann
3990 de499029 Michael Hanselmann
  def Clear(self):
3991 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3992 de499029 Michael Hanselmann

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

3995 de499029 Michael Hanselmann
    """
3996 de499029 Michael Hanselmann
    self.called = False
3997 de499029 Michael Hanselmann
3998 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3999 de499029 Michael Hanselmann
    """Actual signal handling function.
4000 de499029 Michael Hanselmann

4001 de499029 Michael Hanselmann
    """
4002 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
4003 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
4004 de499029 Michael Hanselmann
    self.called = True
4005 a2d2e1a7 Iustin Pop
4006 b9768937 Michael Hanselmann
    if self._wakeup:
4007 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
4008 b9768937 Michael Hanselmann
      self._wakeup.Notify()
4009 b9768937 Michael Hanselmann
4010 92b61ec7 Michael Hanselmann
    if self._handler_fn:
4011 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
4012 92b61ec7 Michael Hanselmann
4013 a2d2e1a7 Iustin Pop
4014 a2d2e1a7 Iustin Pop
class FieldSet(object):
4015 a2d2e1a7 Iustin Pop
  """A simple field set.
4016 a2d2e1a7 Iustin Pop

4017 a2d2e1a7 Iustin Pop
  Among the features are:
4018 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
4019 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
4020 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
4021 a2d2e1a7 Iustin Pop

4022 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
4023 a2d2e1a7 Iustin Pop

4024 a2d2e1a7 Iustin Pop
  """
4025 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
4026 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
4027 a2d2e1a7 Iustin Pop
4028 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
4029 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
4030 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
4031 a2d2e1a7 Iustin Pop
4032 a2d2e1a7 Iustin Pop
  def Matches(self, field):
4033 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
4034 a2d2e1a7 Iustin Pop

4035 a2d2e1a7 Iustin Pop
    @type field: str
4036 a2d2e1a7 Iustin Pop
    @param field: the string to match
4037 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
4038 a2d2e1a7 Iustin Pop

4039 a2d2e1a7 Iustin Pop
    """
4040 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
4041 a2d2e1a7 Iustin Pop
      return m
4042 6c881c52 Iustin Pop
    return None
4043 a2d2e1a7 Iustin Pop
4044 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
4045 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
4046 a2d2e1a7 Iustin Pop

4047 a2d2e1a7 Iustin Pop
    @type items: list
4048 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
4049 a2d2e1a7 Iustin Pop
    @rtype: list
4050 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
4051 a2d2e1a7 Iustin Pop

4052 a2d2e1a7 Iustin Pop
    """
4053 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]
4054 557838c1 René Nussbaumer
4055 557838c1 René Nussbaumer
4056 557838c1 René Nussbaumer
class RunningTimeout(object):
4057 557838c1 René Nussbaumer
  """Class to calculate remaining timeout when doing several operations.
4058 557838c1 René Nussbaumer

4059 557838c1 René Nussbaumer
  """
4060 557838c1 René Nussbaumer
  __slots__ = [
4061 557838c1 René Nussbaumer
    "_allow_negative",
4062 557838c1 René Nussbaumer
    "_start_time",
4063 557838c1 René Nussbaumer
    "_time_fn",
4064 557838c1 René Nussbaumer
    "_timeout",
4065 557838c1 René Nussbaumer
    ]
4066 557838c1 René Nussbaumer
4067 557838c1 René Nussbaumer
  def __init__(self, timeout, allow_negative, _time_fn=time.time):
4068 557838c1 René Nussbaumer
    """Initializes this class.
4069 557838c1 René Nussbaumer

4070 557838c1 René Nussbaumer
    @type timeout: float
4071 557838c1 René Nussbaumer
    @param timeout: Timeout duration
4072 557838c1 René Nussbaumer
    @type allow_negative: bool
4073 557838c1 René Nussbaumer
    @param allow_negative: Whether to return values below zero
4074 557838c1 René Nussbaumer
    @param _time_fn: Time function for unittests
4075 557838c1 René Nussbaumer

4076 557838c1 René Nussbaumer
    """
4077 557838c1 René Nussbaumer
    object.__init__(self)
4078 557838c1 René Nussbaumer
4079 557838c1 René Nussbaumer
    if timeout is not None and timeout < 0.0:
4080 557838c1 René Nussbaumer
      raise ValueError("Timeout must not be negative")
4081 557838c1 René Nussbaumer
4082 557838c1 René Nussbaumer
    self._timeout = timeout
4083 557838c1 René Nussbaumer
    self._allow_negative = allow_negative
4084 557838c1 René Nussbaumer
    self._time_fn = _time_fn
4085 557838c1 René Nussbaumer
4086 557838c1 René Nussbaumer
    self._start_time = None
4087 557838c1 René Nussbaumer
4088 557838c1 René Nussbaumer
  def Remaining(self):
4089 557838c1 René Nussbaumer
    """Returns the remaining timeout.
4090 557838c1 René Nussbaumer

4091 557838c1 René Nussbaumer
    """
4092 557838c1 René Nussbaumer
    if self._timeout is None:
4093 557838c1 René Nussbaumer
      return None
4094 557838c1 René Nussbaumer
4095 557838c1 René Nussbaumer
    # Get start time on first calculation
4096 557838c1 René Nussbaumer
    if self._start_time is None:
4097 557838c1 René Nussbaumer
      self._start_time = self._time_fn()
4098 557838c1 René Nussbaumer
4099 557838c1 René Nussbaumer
    # Calculate remaining time
4100 557838c1 René Nussbaumer
    remaining_timeout = self._start_time + self._timeout - self._time_fn()
4101 557838c1 René Nussbaumer
4102 557838c1 René Nussbaumer
    if not self._allow_negative:
4103 557838c1 René Nussbaumer
      # Ensure timeout is always >= 0
4104 557838c1 René Nussbaumer
      return max(0.0, remaining_timeout)
4105 557838c1 René Nussbaumer
4106 557838c1 René Nussbaumer
    return remaining_timeout