Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 90224407

History | View | Annotate | Download (103.3 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 7c0d6283 Michael Hanselmann
100 a8083063 Iustin Pop
class RunResult(object):
101 58885d79 Iustin Pop
  """Holds the result of running external programs.
102 58885d79 Iustin Pop

103 58885d79 Iustin Pop
  @type exit_code: int
104 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
105 58885d79 Iustin Pop
      didn't exit())
106 58885d79 Iustin Pop
  @type signal: int or None
107 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
108 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
109 58885d79 Iustin Pop
  @type stdout: str
110 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
111 58885d79 Iustin Pop
  @type stderr: str
112 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
113 58885d79 Iustin Pop
  @type failed: boolean
114 58885d79 Iustin Pop
  @ivar failed: True in case the program was
115 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
116 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
117 a8083063 Iustin Pop

118 a8083063 Iustin Pop
  """
119 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
120 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
121 a8083063 Iustin Pop
122 a8083063 Iustin Pop
123 38206f3c Iustin Pop
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
124 a8083063 Iustin Pop
    self.cmd = cmd
125 a8083063 Iustin Pop
    self.exit_code = exit_code
126 38206f3c Iustin Pop
    self.signal = signal_
127 a8083063 Iustin Pop
    self.stdout = stdout
128 a8083063 Iustin Pop
    self.stderr = stderr
129 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
130 a8083063 Iustin Pop
131 a8083063 Iustin Pop
    if self.signal is not None:
132 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
133 a8083063 Iustin Pop
    elif self.exit_code is not None:
134 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
135 a8083063 Iustin Pop
    else:
136 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
137 a8083063 Iustin Pop
138 bb698c1f Iustin Pop
    if self.failed:
139 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
140 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
141 f362096f Iustin Pop
142 a8083063 Iustin Pop
  def _GetOutput(self):
143 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
144 a8083063 Iustin Pop

145 a8083063 Iustin Pop
    """
146 a8083063 Iustin Pop
    return self.stdout + self.stderr
147 a8083063 Iustin Pop
148 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
149 a8083063 Iustin Pop
150 a8083063 Iustin Pop
151 bb3776b4 Michael Hanselmann
def _BuildCmdEnvironment(env, reset):
152 c1dd99d4 Michael Hanselmann
  """Builds the environment for an external program.
153 c1dd99d4 Michael Hanselmann

154 c1dd99d4 Michael Hanselmann
  """
155 bb3776b4 Michael Hanselmann
  if reset:
156 bb3776b4 Michael Hanselmann
    cmd_env = {}
157 bb3776b4 Michael Hanselmann
  else:
158 bb3776b4 Michael Hanselmann
    cmd_env = os.environ.copy()
159 bb3776b4 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
160 bb3776b4 Michael Hanselmann
161 c1dd99d4 Michael Hanselmann
  if env is not None:
162 c1dd99d4 Michael Hanselmann
    cmd_env.update(env)
163 bb3776b4 Michael Hanselmann
164 c1dd99d4 Michael Hanselmann
  return cmd_env
165 c1dd99d4 Michael Hanselmann
166 c1dd99d4 Michael Hanselmann
167 0963d545 Renรฉ Nussbaumer
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False,
168 0963d545 Renรฉ Nussbaumer
           interactive=False):
169 a8083063 Iustin Pop
  """Execute a (shell) command.
170 a8083063 Iustin Pop

171 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
172 a8083063 Iustin Pop
  closed.
173 a8083063 Iustin Pop

174 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
175 36117c2b Iustin Pop
  @param cmd: Command to run
176 2557ff82 Guido Trotter
  @type env: dict
177 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
178 36117c2b Iustin Pop
  @type output: str
179 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
180 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
181 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
182 8797df43 Iustin Pop
  @type cwd: string
183 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
184 8797df43 Iustin Pop
      directory for the command; the default will be /
185 bf4daac9 Guido Trotter
  @type reset_env: boolean
186 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
187 0963d545 Renรฉ Nussbaumer
  @type interactive: boolean
188 0963d545 Renรฉ Nussbaumer
  @param interactive: weather we pipe stdin, stdout and stderr
189 0963d545 Renรฉ Nussbaumer
                      (default behaviour) or run the command interactive
190 36117c2b Iustin Pop
  @rtype: L{RunResult}
191 58885d79 Iustin Pop
  @return: RunResult instance
192 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
193 a8083063 Iustin Pop

194 a8083063 Iustin Pop
  """
195 b74159ee Iustin Pop
  if no_fork:
196 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
197 b74159ee Iustin Pop
198 0963d545 Renรฉ Nussbaumer
  if output and interactive:
199 0963d545 Renรฉ Nussbaumer
    raise errors.ProgrammerError("Parameters 'output' and 'interactive' can"
200 0963d545 Renรฉ Nussbaumer
                                 " not be provided at the same time")
201 0963d545 Renรฉ Nussbaumer
202 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
203 c1dd99d4 Michael Hanselmann
    strcmd = cmd
204 c1dd99d4 Michael Hanselmann
    shell = True
205 c1dd99d4 Michael Hanselmann
  else:
206 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
207 c1dd99d4 Michael Hanselmann
    strcmd = ShellQuoteArgs(cmd)
208 113b55aa Iustin Pop
    shell = False
209 c1dd99d4 Michael Hanselmann
210 c1dd99d4 Michael Hanselmann
  if output:
211 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s, output file '%s'", strcmd, output)
212 113b55aa Iustin Pop
  else:
213 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s", strcmd)
214 2557ff82 Guido Trotter
215 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, reset_env)
216 2557ff82 Guido Trotter
217 c803b052 Iustin Pop
  try:
218 c803b052 Iustin Pop
    if output is None:
219 0963d545 Renรฉ Nussbaumer
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd, interactive)
220 c803b052 Iustin Pop
    else:
221 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
222 c803b052 Iustin Pop
      out = err = ""
223 c803b052 Iustin Pop
  except OSError, err:
224 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
225 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
226 c803b052 Iustin Pop
                               (strcmd, err))
227 c803b052 Iustin Pop
    else:
228 c803b052 Iustin Pop
      raise
229 36117c2b Iustin Pop
230 36117c2b Iustin Pop
  if status >= 0:
231 36117c2b Iustin Pop
    exitcode = status
232 36117c2b Iustin Pop
    signal_ = None
233 36117c2b Iustin Pop
  else:
234 36117c2b Iustin Pop
    exitcode = None
235 36117c2b Iustin Pop
    signal_ = -status
236 36117c2b Iustin Pop
237 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
238 36117c2b Iustin Pop
239 ae59efea Michael Hanselmann
240 0260032c Iustin Pop
def SetupDaemonEnv(cwd="/", umask=077):
241 0260032c Iustin Pop
  """Setup a daemon's environment.
242 0260032c Iustin Pop

243 0260032c Iustin Pop
  This should be called between the first and second fork, due to
244 0260032c Iustin Pop
  setsid usage.
245 0260032c Iustin Pop

246 0260032c Iustin Pop
  @param cwd: the directory to which to chdir
247 0260032c Iustin Pop
  @param umask: the umask to setup
248 0260032c Iustin Pop

249 0260032c Iustin Pop
  """
250 0260032c Iustin Pop
  os.chdir(cwd)
251 0260032c Iustin Pop
  os.umask(umask)
252 0260032c Iustin Pop
  os.setsid()
253 0260032c Iustin Pop
254 0260032c Iustin Pop
255 79634555 Iustin Pop
def SetupDaemonFDs(output_file, output_fd):
256 79634555 Iustin Pop
  """Setups up a daemon's file descriptors.
257 79634555 Iustin Pop

258 79634555 Iustin Pop
  @param output_file: if not None, the file to which to redirect
259 79634555 Iustin Pop
      stdout/stderr
260 79634555 Iustin Pop
  @param output_fd: if not None, the file descriptor for stdout/stderr
261 79634555 Iustin Pop

262 79634555 Iustin Pop
  """
263 79634555 Iustin Pop
  # check that at most one is defined
264 79634555 Iustin Pop
  assert [output_file, output_fd].count(None) >= 1
265 79634555 Iustin Pop
266 79634555 Iustin Pop
  # Open /dev/null (read-only, only for stdin)
267 79634555 Iustin Pop
  devnull_fd = os.open(os.devnull, os.O_RDONLY)
268 79634555 Iustin Pop
269 79634555 Iustin Pop
  if output_fd is not None:
270 79634555 Iustin Pop
    pass
271 79634555 Iustin Pop
  elif output_file is not None:
272 79634555 Iustin Pop
    # Open output file
273 79634555 Iustin Pop
    try:
274 79634555 Iustin Pop
      output_fd = os.open(output_file,
275 79634555 Iustin Pop
                          os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
276 79634555 Iustin Pop
    except EnvironmentError, err:
277 79634555 Iustin Pop
      raise Exception("Opening output file failed: %s" % err)
278 79634555 Iustin Pop
  else:
279 79634555 Iustin Pop
    output_fd = os.open(os.devnull, os.O_WRONLY)
280 79634555 Iustin Pop
281 79634555 Iustin Pop
  # Redirect standard I/O
282 79634555 Iustin Pop
  os.dup2(devnull_fd, 0)
283 79634555 Iustin Pop
  os.dup2(output_fd, 1)
284 79634555 Iustin Pop
  os.dup2(output_fd, 2)
285 79634555 Iustin Pop
286 79634555 Iustin Pop
287 c1dd99d4 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
288 c1dd99d4 Michael Hanselmann
                pidfile=None):
289 c1dd99d4 Michael Hanselmann
  """Start a daemon process after forking twice.
290 c1dd99d4 Michael Hanselmann

291 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
292 c1dd99d4 Michael Hanselmann
  @param cmd: Command to run
293 c1dd99d4 Michael Hanselmann
  @type env: dict
294 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
295 c1dd99d4 Michael Hanselmann
  @type cwd: string
296 c1dd99d4 Michael Hanselmann
  @param cwd: Working directory for the program
297 c1dd99d4 Michael Hanselmann
  @type output: string
298 c1dd99d4 Michael Hanselmann
  @param output: Path to file in which to save the output
299 c1dd99d4 Michael Hanselmann
  @type output_fd: int
300 c1dd99d4 Michael Hanselmann
  @param output_fd: File descriptor for output
301 c1dd99d4 Michael Hanselmann
  @type pidfile: string
302 c1dd99d4 Michael Hanselmann
  @param pidfile: Process ID file
303 c1dd99d4 Michael Hanselmann
  @rtype: int
304 c1dd99d4 Michael Hanselmann
  @return: Daemon process ID
305 c1dd99d4 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
306 c1dd99d4 Michael Hanselmann

307 c1dd99d4 Michael Hanselmann
  """
308 c1dd99d4 Michael Hanselmann
  if no_fork:
309 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("utils.StartDaemon() called with fork()"
310 c1dd99d4 Michael Hanselmann
                                 " disabled")
311 c1dd99d4 Michael Hanselmann
312 c1dd99d4 Michael Hanselmann
  if output and not (bool(output) ^ (output_fd is not None)):
313 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be"
314 c1dd99d4 Michael Hanselmann
                                 " specified")
315 c1dd99d4 Michael Hanselmann
316 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
317 c1dd99d4 Michael Hanselmann
    cmd = ["/bin/sh", "-c", cmd]
318 c1dd99d4 Michael Hanselmann
319 c1dd99d4 Michael Hanselmann
  strcmd = ShellQuoteArgs(cmd)
320 c1dd99d4 Michael Hanselmann
321 c1dd99d4 Michael Hanselmann
  if output:
322 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
323 c1dd99d4 Michael Hanselmann
  else:
324 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s", strcmd)
325 c1dd99d4 Michael Hanselmann
326 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, False)
327 c1dd99d4 Michael Hanselmann
328 c1dd99d4 Michael Hanselmann
  # Create pipe for sending PID back
329 c1dd99d4 Michael Hanselmann
  (pidpipe_read, pidpipe_write) = os.pipe()
330 c1dd99d4 Michael Hanselmann
  try:
331 c1dd99d4 Michael Hanselmann
    try:
332 c1dd99d4 Michael Hanselmann
      # Create pipe for sending error messages
333 c1dd99d4 Michael Hanselmann
      (errpipe_read, errpipe_write) = os.pipe()
334 c1dd99d4 Michael Hanselmann
      try:
335 c1dd99d4 Michael Hanselmann
        try:
336 c1dd99d4 Michael Hanselmann
          # First fork
337 c1dd99d4 Michael Hanselmann
          pid = os.fork()
338 c1dd99d4 Michael Hanselmann
          if pid == 0:
339 c1dd99d4 Michael Hanselmann
            try:
340 c1dd99d4 Michael Hanselmann
              # Child process, won't return
341 e0bb431e Michael Hanselmann
              _StartDaemonChild(errpipe_read, errpipe_write,
342 e0bb431e Michael Hanselmann
                                pidpipe_read, pidpipe_write,
343 e0bb431e Michael Hanselmann
                                cmd, cmd_env, cwd,
344 e0bb431e Michael Hanselmann
                                output, output_fd, pidfile)
345 c1dd99d4 Michael Hanselmann
            finally:
346 c1dd99d4 Michael Hanselmann
              # Well, maybe child process failed
347 e0bb431e Michael Hanselmann
              os._exit(1) # pylint: disable-msg=W0212
348 c1dd99d4 Michael Hanselmann
        finally:
349 c1dd99d4 Michael Hanselmann
          _CloseFDNoErr(errpipe_write)
350 c1dd99d4 Michael Hanselmann
351 b78aa8c2 Iustin Pop
        # Wait for daemon to be started (or an error message to
352 b78aa8c2 Iustin Pop
        # arrive) and read up to 100 KB as an error message
353 c1dd99d4 Michael Hanselmann
        errormsg = RetryOnSignal(os.read, errpipe_read, 100 * 1024)
354 c1dd99d4 Michael Hanselmann
      finally:
355 c1dd99d4 Michael Hanselmann
        _CloseFDNoErr(errpipe_read)
356 c1dd99d4 Michael Hanselmann
    finally:
357 c1dd99d4 Michael Hanselmann
      _CloseFDNoErr(pidpipe_write)
358 c1dd99d4 Michael Hanselmann
359 c1dd99d4 Michael Hanselmann
    # Read up to 128 bytes for PID
360 c1dd99d4 Michael Hanselmann
    pidtext = RetryOnSignal(os.read, pidpipe_read, 128)
361 c1dd99d4 Michael Hanselmann
  finally:
362 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
363 c1dd99d4 Michael Hanselmann
364 c1dd99d4 Michael Hanselmann
  # Try to avoid zombies by waiting for child process
365 c1dd99d4 Michael Hanselmann
  try:
366 c1dd99d4 Michael Hanselmann
    os.waitpid(pid, 0)
367 c1dd99d4 Michael Hanselmann
  except OSError:
368 c1dd99d4 Michael Hanselmann
    pass
369 c1dd99d4 Michael Hanselmann
370 c1dd99d4 Michael Hanselmann
  if errormsg:
371 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error when starting daemon process: %r" %
372 c1dd99d4 Michael Hanselmann
                             errormsg)
373 c1dd99d4 Michael Hanselmann
374 c1dd99d4 Michael Hanselmann
  try:
375 c1dd99d4 Michael Hanselmann
    return int(pidtext)
376 c1dd99d4 Michael Hanselmann
  except (ValueError, TypeError), err:
377 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error while trying to parse PID %r: %s" %
378 c1dd99d4 Michael Hanselmann
                             (pidtext, err))
379 c1dd99d4 Michael Hanselmann
380 c1dd99d4 Michael Hanselmann
381 e0bb431e Michael Hanselmann
def _StartDaemonChild(errpipe_read, errpipe_write,
382 e0bb431e Michael Hanselmann
                      pidpipe_read, pidpipe_write,
383 e0bb431e Michael Hanselmann
                      args, env, cwd,
384 e0bb431e Michael Hanselmann
                      output, fd_output, pidfile):
385 c1dd99d4 Michael Hanselmann
  """Child process for starting daemon.
386 c1dd99d4 Michael Hanselmann

387 c1dd99d4 Michael Hanselmann
  """
388 c1dd99d4 Michael Hanselmann
  try:
389 c1dd99d4 Michael Hanselmann
    # Close parent's side
390 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(errpipe_read)
391 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
392 c1dd99d4 Michael Hanselmann
393 c1dd99d4 Michael Hanselmann
    # First child process
394 0260032c Iustin Pop
    SetupDaemonEnv()
395 c1dd99d4 Michael Hanselmann
396 c1dd99d4 Michael Hanselmann
    # And fork for the second time
397 c1dd99d4 Michael Hanselmann
    pid = os.fork()
398 c1dd99d4 Michael Hanselmann
    if pid != 0:
399 c1dd99d4 Michael Hanselmann
      # Exit first child process
400 c1dd99d4 Michael Hanselmann
      os._exit(0) # pylint: disable-msg=W0212
401 c1dd99d4 Michael Hanselmann
402 0260032c Iustin Pop
    # Make sure pipe is closed on execv* (and thereby notifies
403 0260032c Iustin Pop
    # original process)
404 c1dd99d4 Michael Hanselmann
    SetCloseOnExecFlag(errpipe_write, True)
405 c1dd99d4 Michael Hanselmann
406 c1dd99d4 Michael Hanselmann
    # List of file descriptors to be left open
407 c1dd99d4 Michael Hanselmann
    noclose_fds = [errpipe_write]
408 c1dd99d4 Michael Hanselmann
409 c1dd99d4 Michael Hanselmann
    # Open PID file
410 c1dd99d4 Michael Hanselmann
    if pidfile:
411 5c4d37f9 Iustin Pop
      fd_pidfile = WritePidFile(pidfile)
412 c1dd99d4 Michael Hanselmann
413 c1dd99d4 Michael Hanselmann
      # Keeping the file open to hold the lock
414 c1dd99d4 Michael Hanselmann
      noclose_fds.append(fd_pidfile)
415 c1dd99d4 Michael Hanselmann
416 c1dd99d4 Michael Hanselmann
      SetCloseOnExecFlag(fd_pidfile, False)
417 c1dd99d4 Michael Hanselmann
    else:
418 c1dd99d4 Michael Hanselmann
      fd_pidfile = None
419 c1dd99d4 Michael Hanselmann
420 79634555 Iustin Pop
    SetupDaemonFDs(output, fd_output)
421 c1dd99d4 Michael Hanselmann
422 c1dd99d4 Michael Hanselmann
    # Send daemon PID to parent
423 c1dd99d4 Michael Hanselmann
    RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
424 c1dd99d4 Michael Hanselmann
425 c1dd99d4 Michael Hanselmann
    # Close all file descriptors except stdio and error message pipe
426 c1dd99d4 Michael Hanselmann
    CloseFDs(noclose_fds=noclose_fds)
427 c1dd99d4 Michael Hanselmann
428 c1dd99d4 Michael Hanselmann
    # Change working directory
429 c1dd99d4 Michael Hanselmann
    os.chdir(cwd)
430 c1dd99d4 Michael Hanselmann
431 c1dd99d4 Michael Hanselmann
    if env is None:
432 c1dd99d4 Michael Hanselmann
      os.execvp(args[0], args)
433 c1dd99d4 Michael Hanselmann
    else:
434 c1dd99d4 Michael Hanselmann
      os.execvpe(args[0], args, env)
435 c1dd99d4 Michael Hanselmann
  except: # pylint: disable-msg=W0702
436 c1dd99d4 Michael Hanselmann
    try:
437 c1dd99d4 Michael Hanselmann
      # Report errors to original process
438 ed3920e3 Iustin Pop
      WriteErrorToFD(errpipe_write, str(sys.exc_info()[1]))
439 c1dd99d4 Michael Hanselmann
    except: # pylint: disable-msg=W0702
440 c1dd99d4 Michael Hanselmann
      # Ignore errors in error handling
441 c1dd99d4 Michael Hanselmann
      pass
442 c1dd99d4 Michael Hanselmann
443 c1dd99d4 Michael Hanselmann
  os._exit(1) # pylint: disable-msg=W0212
444 c1dd99d4 Michael Hanselmann
445 c1dd99d4 Michael Hanselmann
446 ed3920e3 Iustin Pop
def WriteErrorToFD(fd, err):
447 ed3920e3 Iustin Pop
  """Possibly write an error message to a fd.
448 ed3920e3 Iustin Pop

449 ed3920e3 Iustin Pop
  @type fd: None or int (file descriptor)
450 ed3920e3 Iustin Pop
  @param fd: if not None, the error will be written to this fd
451 ed3920e3 Iustin Pop
  @param err: string, the error message
452 ed3920e3 Iustin Pop

453 ed3920e3 Iustin Pop
  """
454 ed3920e3 Iustin Pop
  if fd is None:
455 ed3920e3 Iustin Pop
    return
456 ed3920e3 Iustin Pop
457 ed3920e3 Iustin Pop
  if not err:
458 ed3920e3 Iustin Pop
    err = "<unknown error>"
459 ed3920e3 Iustin Pop
460 ed3920e3 Iustin Pop
  RetryOnSignal(os.write, fd, err)
461 ed3920e3 Iustin Pop
462 ed3920e3 Iustin Pop
463 0963d545 Renรฉ Nussbaumer
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive):
464 36117c2b Iustin Pop
  """Run a command and return its output.
465 36117c2b Iustin Pop

466 36117c2b Iustin Pop
  @type  cmd: string or list
467 36117c2b Iustin Pop
  @param cmd: Command to run
468 36117c2b Iustin Pop
  @type env: dict
469 36117c2b Iustin Pop
  @param env: The environment to use
470 36117c2b Iustin Pop
  @type via_shell: bool
471 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
472 8797df43 Iustin Pop
  @type cwd: string
473 8797df43 Iustin Pop
  @param cwd: the working directory for the program
474 0963d545 Renรฉ Nussbaumer
  @type interactive: boolean
475 0963d545 Renรฉ Nussbaumer
  @param interactive: Run command interactive (without piping)
476 36117c2b Iustin Pop
  @rtype: tuple
477 36117c2b Iustin Pop
  @return: (out, err, status)
478 36117c2b Iustin Pop

479 36117c2b Iustin Pop
  """
480 9c233417 Iustin Pop
  poller = select.poll()
481 0963d545 Renรฉ Nussbaumer
482 0963d545 Renรฉ Nussbaumer
  stderr = subprocess.PIPE
483 0963d545 Renรฉ Nussbaumer
  stdout = subprocess.PIPE
484 0963d545 Renรฉ Nussbaumer
  stdin = subprocess.PIPE
485 0963d545 Renรฉ Nussbaumer
486 0963d545 Renรฉ Nussbaumer
  if interactive:
487 0963d545 Renรฉ Nussbaumer
    stderr = stdout = stdin = None
488 0963d545 Renรฉ Nussbaumer
489 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
490 0963d545 Renรฉ Nussbaumer
                           stderr=stderr,
491 0963d545 Renรฉ Nussbaumer
                           stdout=stdout,
492 0963d545 Renรฉ Nussbaumer
                           stdin=stdin,
493 8797df43 Iustin Pop
                           close_fds=True, env=env,
494 8797df43 Iustin Pop
                           cwd=cwd)
495 113b55aa Iustin Pop
496 9c233417 Iustin Pop
  out = StringIO()
497 9c233417 Iustin Pop
  err = StringIO()
498 0963d545 Renรฉ Nussbaumer
  if not interactive:
499 0963d545 Renรฉ Nussbaumer
    child.stdin.close()
500 0963d545 Renรฉ Nussbaumer
    poller.register(child.stdout, select.POLLIN)
501 0963d545 Renรฉ Nussbaumer
    poller.register(child.stderr, select.POLLIN)
502 0963d545 Renรฉ Nussbaumer
    fdmap = {
503 0963d545 Renรฉ Nussbaumer
      child.stdout.fileno(): (out, child.stdout),
504 0963d545 Renรฉ Nussbaumer
      child.stderr.fileno(): (err, child.stderr),
505 0963d545 Renรฉ Nussbaumer
      }
506 0963d545 Renรฉ Nussbaumer
    for fd in fdmap:
507 0963d545 Renรฉ Nussbaumer
      SetNonblockFlag(fd, True)
508 0963d545 Renรฉ Nussbaumer
509 0963d545 Renรฉ Nussbaumer
    while fdmap:
510 0963d545 Renรฉ Nussbaumer
      pollresult = RetryOnSignal(poller.poll)
511 0963d545 Renรฉ Nussbaumer
512 0963d545 Renรฉ Nussbaumer
      for fd, event in pollresult:
513 0963d545 Renรฉ Nussbaumer
        if event & select.POLLIN or event & select.POLLPRI:
514 0963d545 Renรฉ Nussbaumer
          data = fdmap[fd][1].read()
515 0963d545 Renรฉ Nussbaumer
          # no data from read signifies EOF (the same as POLLHUP)
516 0963d545 Renรฉ Nussbaumer
          if not data:
517 0963d545 Renรฉ Nussbaumer
            poller.unregister(fd)
518 0963d545 Renรฉ Nussbaumer
            del fdmap[fd]
519 0963d545 Renรฉ Nussbaumer
            continue
520 0963d545 Renรฉ Nussbaumer
          fdmap[fd][0].write(data)
521 0963d545 Renรฉ Nussbaumer
        if (event & select.POLLNVAL or event & select.POLLHUP or
522 0963d545 Renรฉ Nussbaumer
            event & select.POLLERR):
523 9c233417 Iustin Pop
          poller.unregister(fd)
524 9c233417 Iustin Pop
          del fdmap[fd]
525 9c233417 Iustin Pop
526 9c233417 Iustin Pop
  out = out.getvalue()
527 9c233417 Iustin Pop
  err = err.getvalue()
528 a8083063 Iustin Pop
529 a8083063 Iustin Pop
  status = child.wait()
530 36117c2b Iustin Pop
  return out, err, status
531 a8083063 Iustin Pop
532 36117c2b Iustin Pop
533 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
534 36117c2b Iustin Pop
  """Run a command and save its output to a file.
535 36117c2b Iustin Pop

536 36117c2b Iustin Pop
  @type  cmd: string or list
537 36117c2b Iustin Pop
  @param cmd: Command to run
538 36117c2b Iustin Pop
  @type env: dict
539 36117c2b Iustin Pop
  @param env: The environment to use
540 36117c2b Iustin Pop
  @type via_shell: bool
541 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
542 36117c2b Iustin Pop
  @type output: str
543 36117c2b Iustin Pop
  @param output: the filename in which to save the output
544 8797df43 Iustin Pop
  @type cwd: string
545 8797df43 Iustin Pop
  @param cwd: the working directory for the program
546 36117c2b Iustin Pop
  @rtype: int
547 36117c2b Iustin Pop
  @return: the exit status
548 36117c2b Iustin Pop

549 36117c2b Iustin Pop
  """
550 36117c2b Iustin Pop
  fh = open(output, "a")
551 36117c2b Iustin Pop
  try:
552 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
553 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
554 36117c2b Iustin Pop
                             stdout=fh,
555 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
556 8797df43 Iustin Pop
                             close_fds=True, env=env,
557 8797df43 Iustin Pop
                             cwd=cwd)
558 36117c2b Iustin Pop
559 36117c2b Iustin Pop
    child.stdin.close()
560 36117c2b Iustin Pop
    status = child.wait()
561 36117c2b Iustin Pop
  finally:
562 36117c2b Iustin Pop
    fh.close()
563 36117c2b Iustin Pop
  return status
564 a8083063 Iustin Pop
565 a8083063 Iustin Pop
566 73027ed2 Michael Hanselmann
def SetCloseOnExecFlag(fd, enable):
567 73027ed2 Michael Hanselmann
  """Sets or unsets the close-on-exec flag on a file descriptor.
568 73027ed2 Michael Hanselmann

569 73027ed2 Michael Hanselmann
  @type fd: int
570 73027ed2 Michael Hanselmann
  @param fd: File descriptor
571 73027ed2 Michael Hanselmann
  @type enable: bool
572 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
573 73027ed2 Michael Hanselmann

574 73027ed2 Michael Hanselmann
  """
575 73027ed2 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
576 73027ed2 Michael Hanselmann
577 73027ed2 Michael Hanselmann
  if enable:
578 73027ed2 Michael Hanselmann
    flags |= fcntl.FD_CLOEXEC
579 73027ed2 Michael Hanselmann
  else:
580 73027ed2 Michael Hanselmann
    flags &= ~fcntl.FD_CLOEXEC
581 73027ed2 Michael Hanselmann
582 73027ed2 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
583 73027ed2 Michael Hanselmann
584 73027ed2 Michael Hanselmann
585 287a1740 Michael Hanselmann
def SetNonblockFlag(fd, enable):
586 287a1740 Michael Hanselmann
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
587 287a1740 Michael Hanselmann

588 287a1740 Michael Hanselmann
  @type fd: int
589 287a1740 Michael Hanselmann
  @param fd: File descriptor
590 287a1740 Michael Hanselmann
  @type enable: bool
591 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
592 287a1740 Michael Hanselmann

593 287a1740 Michael Hanselmann
  """
594 287a1740 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
595 287a1740 Michael Hanselmann
596 287a1740 Michael Hanselmann
  if enable:
597 287a1740 Michael Hanselmann
    flags |= os.O_NONBLOCK
598 287a1740 Michael Hanselmann
  else:
599 287a1740 Michael Hanselmann
    flags &= ~os.O_NONBLOCK
600 287a1740 Michael Hanselmann
601 287a1740 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
602 287a1740 Michael Hanselmann
603 287a1740 Michael Hanselmann
604 edcb5d9e Michael Hanselmann
def RetryOnSignal(fn, *args, **kwargs):
605 edcb5d9e Michael Hanselmann
  """Calls a function again if it failed due to EINTR.
606 edcb5d9e Michael Hanselmann

607 edcb5d9e Michael Hanselmann
  """
608 edcb5d9e Michael Hanselmann
  while True:
609 edcb5d9e Michael Hanselmann
    try:
610 edcb5d9e Michael Hanselmann
      return fn(*args, **kwargs)
611 965d0e5b Guido Trotter
    except EnvironmentError, err:
612 edcb5d9e Michael Hanselmann
      if err.errno != errno.EINTR:
613 edcb5d9e Michael Hanselmann
        raise
614 965d0e5b Guido Trotter
    except (socket.error, select.error), err:
615 965d0e5b Guido Trotter
      # In python 2.6 and above select.error is an IOError, so it's handled
616 965d0e5b Guido Trotter
      # above, in 2.5 and below it's not, and it's handled here.
617 edcb5d9e Michael Hanselmann
      if not (err.args and err.args[0] == errno.EINTR):
618 edcb5d9e Michael Hanselmann
        raise
619 edcb5d9e Michael Hanselmann
620 edcb5d9e Michael Hanselmann
621 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
622 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
623 6bb65e3a Guido Trotter

624 6bb65e3a Guido Trotter
  @type dir_name: string
625 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
626 6bb65e3a Guido Trotter
  @type env: dict
627 6bb65e3a Guido Trotter
  @param env: The environment to use
628 6bb65e3a Guido Trotter
  @type reset_env: boolean
629 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
630 6bb65e3a Guido Trotter
  @rtype: list of tuples
631 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
632 6bb65e3a Guido Trotter

633 6bb65e3a Guido Trotter
  """
634 6bb65e3a Guido Trotter
  rr = []
635 6bb65e3a Guido Trotter
636 6bb65e3a Guido Trotter
  try:
637 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
638 6bb65e3a Guido Trotter
  except OSError, err:
639 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
640 6bb65e3a Guido Trotter
    return rr
641 6bb65e3a Guido Trotter
642 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
643 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
644 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
645 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
646 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
647 6bb65e3a Guido Trotter
    else:
648 6bb65e3a Guido Trotter
      try:
649 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
650 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
651 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
652 6bb65e3a Guido Trotter
      else:
653 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
654 6bb65e3a Guido Trotter
655 6bb65e3a Guido Trotter
  return rr
656 6bb65e3a Guido Trotter
657 6bb65e3a Guido Trotter
658 a8083063 Iustin Pop
def RemoveFile(filename):
659 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
660 a8083063 Iustin Pop

661 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
662 a8083063 Iustin Pop
  errors are passed.
663 a8083063 Iustin Pop

664 58885d79 Iustin Pop
  @type filename: str
665 58885d79 Iustin Pop
  @param filename: the file to be removed
666 58885d79 Iustin Pop

667 a8083063 Iustin Pop
  """
668 a8083063 Iustin Pop
  try:
669 a8083063 Iustin Pop
    os.unlink(filename)
670 a8083063 Iustin Pop
  except OSError, err:
671 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
672 a8083063 Iustin Pop
      raise
673 a8083063 Iustin Pop
674 72087dcd Balazs Lecz
675 72087dcd Balazs Lecz
def RemoveDir(dirname):
676 72087dcd Balazs Lecz
  """Remove an empty directory.
677 72087dcd Balazs Lecz

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

682 72087dcd Balazs Lecz
  @type dirname: str
683 72087dcd Balazs Lecz
  @param dirname: the empty directory to be removed
684 72087dcd Balazs Lecz

685 72087dcd Balazs Lecz
  """
686 72087dcd Balazs Lecz
  try:
687 72087dcd Balazs Lecz
    os.rmdir(dirname)
688 72087dcd Balazs Lecz
  except OSError, err:
689 72087dcd Balazs Lecz
    if err.errno != errno.ENOENT:
690 72087dcd Balazs Lecz
      raise
691 72087dcd Balazs Lecz
692 a8083063 Iustin Pop
693 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
694 6e797216 Michael Hanselmann
  """Renames a file.
695 6e797216 Michael Hanselmann

696 6e797216 Michael Hanselmann
  @type old: string
697 6e797216 Michael Hanselmann
  @param old: Original path
698 6e797216 Michael Hanselmann
  @type new: string
699 6e797216 Michael Hanselmann
  @param new: New path
700 6e797216 Michael Hanselmann
  @type mkdir: bool
701 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
702 6e797216 Michael Hanselmann
  @type mkdir_mode: int
703 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
704 6e797216 Michael Hanselmann

705 6e797216 Michael Hanselmann
  """
706 6e797216 Michael Hanselmann
  try:
707 6e797216 Michael Hanselmann
    return os.rename(old, new)
708 6e797216 Michael Hanselmann
  except OSError, err:
709 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
710 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
711 6e797216 Michael Hanselmann
    # as efficient.
712 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
713 6e797216 Michael Hanselmann
      # Create directory and try again
714 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
715 a426508d Michael Hanselmann
716 6e797216 Michael Hanselmann
      return os.rename(old, new)
717 a426508d Michael Hanselmann
718 6e797216 Michael Hanselmann
    raise
719 6e797216 Michael Hanselmann
720 6e797216 Michael Hanselmann
721 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
722 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
723 76e5f8b5 Michael Hanselmann

724 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
725 76e5f8b5 Michael Hanselmann
  before Python 2.5.
726 76e5f8b5 Michael Hanselmann

727 76e5f8b5 Michael Hanselmann
  """
728 76e5f8b5 Michael Hanselmann
  try:
729 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
730 76e5f8b5 Michael Hanselmann
  except OSError, err:
731 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
732 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
733 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
734 76e5f8b5 Michael Hanselmann
      raise
735 76e5f8b5 Michael Hanselmann
736 76e5f8b5 Michael Hanselmann
737 055f822b Michael Hanselmann
def ResetTempfileModule():
738 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
739 055f822b Michael Hanselmann

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

746 055f822b Michael Hanselmann
  """
747 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
748 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
749 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
750 055f822b Michael Hanselmann
    try:
751 055f822b Michael Hanselmann
      # Reset random name generator
752 055f822b Michael Hanselmann
      tempfile._name_sequence = None
753 055f822b Michael Hanselmann
    finally:
754 055f822b Michael Hanselmann
      tempfile._once_lock.release()
755 055f822b Michael Hanselmann
  else:
756 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
757 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
758 055f822b Michael Hanselmann
759 055f822b Michael Hanselmann
760 a8083063 Iustin Pop
def _FingerprintFile(filename):
761 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
762 a8083063 Iustin Pop

763 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
764 a8083063 Iustin Pop
  instead.
765 a8083063 Iustin Pop

766 58885d79 Iustin Pop
  @type filename: str
767 58885d79 Iustin Pop
  @param filename: the filename to checksum
768 58885d79 Iustin Pop
  @rtype: str
769 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
770 58885d79 Iustin Pop
      of the file
771 a8083063 Iustin Pop

772 a8083063 Iustin Pop
  """
773 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
774 a8083063 Iustin Pop
    return None
775 a8083063 Iustin Pop
776 a8083063 Iustin Pop
  f = open(filename)
777 a8083063 Iustin Pop
778 716a32cb Guido Trotter
  fp = compat.sha1_hash()
779 a8083063 Iustin Pop
  while True:
780 a8083063 Iustin Pop
    data = f.read(4096)
781 a8083063 Iustin Pop
    if not data:
782 a8083063 Iustin Pop
      break
783 a8083063 Iustin Pop
784 a8083063 Iustin Pop
    fp.update(data)
785 a8083063 Iustin Pop
786 a8083063 Iustin Pop
  return fp.hexdigest()
787 a8083063 Iustin Pop
788 a8083063 Iustin Pop
789 a8083063 Iustin Pop
def FingerprintFiles(files):
790 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
791 a8083063 Iustin Pop

792 58885d79 Iustin Pop
  @type files: list
793 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
794 58885d79 Iustin Pop
  @rtype: dict
795 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
796 58885d79 Iustin Pop
      existing files
797 a8083063 Iustin Pop

798 a8083063 Iustin Pop
  """
799 a8083063 Iustin Pop
  ret = {}
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
  for filename in files:
802 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
803 a8083063 Iustin Pop
    if cksum:
804 a8083063 Iustin Pop
      ret[filename] = cksum
805 a8083063 Iustin Pop
806 a8083063 Iustin Pop
  return ret
807 a8083063 Iustin Pop
808 a8083063 Iustin Pop
809 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
810 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
811 a5728081 Guido Trotter

812 a5728081 Guido Trotter
  @type target: dict
813 a5728081 Guido Trotter
  @param target: the dict to update
814 a5728081 Guido Trotter
  @type key_types: dict
815 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
816 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
817 a5728081 Guido Trotter
  @type allowed_values: list
818 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
819 a5728081 Guido Trotter

820 a5728081 Guido Trotter
  """
821 a5728081 Guido Trotter
  if allowed_values is None:
822 a5728081 Guido Trotter
    allowed_values = []
823 a5728081 Guido Trotter
824 8b46606c Guido Trotter
  if not isinstance(target, dict):
825 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
826 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
827 8b46606c Guido Trotter
828 a5728081 Guido Trotter
  for key in target:
829 a5728081 Guido Trotter
    if key not in key_types:
830 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
831 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
832 a5728081 Guido Trotter
833 a5728081 Guido Trotter
    if target[key] in allowed_values:
834 a5728081 Guido Trotter
      continue
835 a5728081 Guido Trotter
836 29921401 Iustin Pop
    ktype = key_types[key]
837 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
838 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
839 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
840 a5728081 Guido Trotter
841 59525e1f Michael Hanselmann
    if ktype in (constants.VTYPE_STRING, constants.VTYPE_MAYBE_STRING):
842 59525e1f Michael Hanselmann
      if target[key] is None and ktype == constants.VTYPE_MAYBE_STRING:
843 59525e1f Michael Hanselmann
        pass
844 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
845 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
846 a5728081 Guido Trotter
          target[key] = ''
847 a5728081 Guido Trotter
        else:
848 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
849 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
850 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
851 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
852 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
853 a5728081 Guido Trotter
          target[key] = False
854 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
855 a5728081 Guido Trotter
          target[key] = True
856 a5728081 Guido Trotter
        else:
857 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
858 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
859 a5728081 Guido Trotter
      elif target[key]:
860 a5728081 Guido Trotter
        target[key] = True
861 a5728081 Guido Trotter
      else:
862 a5728081 Guido Trotter
        target[key] = False
863 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
864 a5728081 Guido Trotter
      try:
865 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
866 a5728081 Guido Trotter
      except errors.UnitParseError, err:
867 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
868 a5728081 Guido Trotter
              (key, target[key], err)
869 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
870 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
871 a5728081 Guido Trotter
      try:
872 a5728081 Guido Trotter
        target[key] = int(target[key])
873 a5728081 Guido Trotter
      except (ValueError, TypeError):
874 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
875 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
876 a5728081 Guido Trotter
877 a5728081 Guido Trotter
878 a01b500b Michael Hanselmann
def _GetProcStatusPath(pid):
879 a01b500b Michael Hanselmann
  """Returns the path for a PID's proc status file.
880 a01b500b Michael Hanselmann

881 a01b500b Michael Hanselmann
  @type pid: int
882 a01b500b Michael Hanselmann
  @param pid: Process ID
883 a01b500b Michael Hanselmann
  @rtype: string
884 a01b500b Michael Hanselmann

885 a01b500b Michael Hanselmann
  """
886 a01b500b Michael Hanselmann
  return "/proc/%d/status" % pid
887 a01b500b Michael Hanselmann
888 a01b500b Michael Hanselmann
889 a8083063 Iustin Pop
def IsProcessAlive(pid):
890 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
891 a8083063 Iustin Pop

892 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
893 44bf25ff Iustin Pop
      will be returned as alive
894 58885d79 Iustin Pop
  @type pid: int
895 58885d79 Iustin Pop
  @param pid: the process ID to check
896 58885d79 Iustin Pop
  @rtype: boolean
897 58885d79 Iustin Pop
  @return: True if the process exists
898 a8083063 Iustin Pop

899 a8083063 Iustin Pop
  """
900 5ef5ea45 Guido Trotter
  def _TryStat(name):
901 5ef5ea45 Guido Trotter
    try:
902 5ef5ea45 Guido Trotter
      os.stat(name)
903 5ef5ea45 Guido Trotter
      return True
904 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
905 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
906 5ef5ea45 Guido Trotter
        return False
907 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
908 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
909 5ef5ea45 Guido Trotter
      raise
910 5ef5ea45 Guido Trotter
911 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
912 d9f311d7 Iustin Pop
  if pid <= 0:
913 d9f311d7 Iustin Pop
    return False
914 d9f311d7 Iustin Pop
915 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
916 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
917 a8083063 Iustin Pop
  try:
918 a01b500b Michael Hanselmann
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
919 a01b500b Michael Hanselmann
                 args=[_GetProcStatusPath(pid)])
920 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
921 5ef5ea45 Guido Trotter
    err.RaiseInner()
922 a8083063 Iustin Pop
923 a8083063 Iustin Pop
924 a01b500b Michael Hanselmann
def _ParseSigsetT(sigset):
925 a01b500b Michael Hanselmann
  """Parse a rendered sigset_t value.
926 a01b500b Michael Hanselmann

927 a01b500b Michael Hanselmann
  This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
928 a01b500b Michael Hanselmann
  function.
929 a01b500b Michael Hanselmann

930 a01b500b Michael Hanselmann
  @type sigset: string
931 a01b500b Michael Hanselmann
  @param sigset: Rendered signal set from /proc/$pid/status
932 a01b500b Michael Hanselmann
  @rtype: set
933 a01b500b Michael Hanselmann
  @return: Set of all enabled signal numbers
934 a01b500b Michael Hanselmann

935 a01b500b Michael Hanselmann
  """
936 a01b500b Michael Hanselmann
  result = set()
937 a01b500b Michael Hanselmann
938 a01b500b Michael Hanselmann
  signum = 0
939 a01b500b Michael Hanselmann
  for ch in reversed(sigset):
940 a01b500b Michael Hanselmann
    chv = int(ch, 16)
941 a01b500b Michael Hanselmann
942 a01b500b Michael Hanselmann
    # The following could be done in a loop, but it's easier to read and
943 a01b500b Michael Hanselmann
    # understand in the unrolled form
944 a01b500b Michael Hanselmann
    if chv & 1:
945 a01b500b Michael Hanselmann
      result.add(signum + 1)
946 a01b500b Michael Hanselmann
    if chv & 2:
947 a01b500b Michael Hanselmann
      result.add(signum + 2)
948 a01b500b Michael Hanselmann
    if chv & 4:
949 a01b500b Michael Hanselmann
      result.add(signum + 3)
950 a01b500b Michael Hanselmann
    if chv & 8:
951 a01b500b Michael Hanselmann
      result.add(signum + 4)
952 a01b500b Michael Hanselmann
953 a01b500b Michael Hanselmann
    signum += 4
954 a01b500b Michael Hanselmann
955 a01b500b Michael Hanselmann
  return result
956 a01b500b Michael Hanselmann
957 a01b500b Michael Hanselmann
958 a01b500b Michael Hanselmann
def _GetProcStatusField(pstatus, field):
959 a01b500b Michael Hanselmann
  """Retrieves a field from the contents of a proc status file.
960 a01b500b Michael Hanselmann

961 a01b500b Michael Hanselmann
  @type pstatus: string
962 a01b500b Michael Hanselmann
  @param pstatus: Contents of /proc/$pid/status
963 a01b500b Michael Hanselmann
  @type field: string
964 a01b500b Michael Hanselmann
  @param field: Name of field whose value should be returned
965 a01b500b Michael Hanselmann
  @rtype: string
966 a01b500b Michael Hanselmann

967 a01b500b Michael Hanselmann
  """
968 a01b500b Michael Hanselmann
  for line in pstatus.splitlines():
969 a01b500b Michael Hanselmann
    parts = line.split(":", 1)
970 a01b500b Michael Hanselmann
971 a01b500b Michael Hanselmann
    if len(parts) < 2 or parts[0] != field:
972 a01b500b Michael Hanselmann
      continue
973 a01b500b Michael Hanselmann
974 a01b500b Michael Hanselmann
    return parts[1].strip()
975 a01b500b Michael Hanselmann
976 a01b500b Michael Hanselmann
  return None
977 a01b500b Michael Hanselmann
978 a01b500b Michael Hanselmann
979 a01b500b Michael Hanselmann
def IsProcessHandlingSignal(pid, signum, status_path=None):
980 a01b500b Michael Hanselmann
  """Checks whether a process is handling a signal.
981 a01b500b Michael Hanselmann

982 a01b500b Michael Hanselmann
  @type pid: int
983 a01b500b Michael Hanselmann
  @param pid: Process ID
984 a01b500b Michael Hanselmann
  @type signum: int
985 a01b500b Michael Hanselmann
  @param signum: Signal number
986 a01b500b Michael Hanselmann
  @rtype: bool
987 a01b500b Michael Hanselmann

988 a01b500b Michael Hanselmann
  """
989 a01b500b Michael Hanselmann
  if status_path is None:
990 a01b500b Michael Hanselmann
    status_path = _GetProcStatusPath(pid)
991 a01b500b Michael Hanselmann
992 a01b500b Michael Hanselmann
  try:
993 a01b500b Michael Hanselmann
    proc_status = ReadFile(status_path)
994 a01b500b Michael Hanselmann
  except EnvironmentError, err:
995 a01b500b Michael Hanselmann
    # In at least one case, reading /proc/$pid/status failed with ESRCH.
996 a01b500b Michael Hanselmann
    if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
997 a01b500b Michael Hanselmann
      return False
998 a01b500b Michael Hanselmann
    raise
999 a01b500b Michael Hanselmann
1000 a01b500b Michael Hanselmann
  sigcgt = _GetProcStatusField(proc_status, "SigCgt")
1001 a01b500b Michael Hanselmann
  if sigcgt is None:
1002 a01b500b Michael Hanselmann
    raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
1003 a01b500b Michael Hanselmann
1004 a01b500b Michael Hanselmann
  # Now check whether signal is handled
1005 a01b500b Michael Hanselmann
  return signum in _ParseSigsetT(sigcgt)
1006 a01b500b Michael Hanselmann
1007 a01b500b Michael Hanselmann
1008 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
1009 58885d79 Iustin Pop
  """Read a pid from a file.
1010 fee80e90 Guido Trotter

1011 58885d79 Iustin Pop
  @type  pidfile: string
1012 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
1013 58885d79 Iustin Pop
  @rtype: int
1014 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
1015 d9f311d7 Iustin Pop
           otherwise 0
1016 fee80e90 Guido Trotter

1017 fee80e90 Guido Trotter
  """
1018 fee80e90 Guido Trotter
  try:
1019 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
1020 d9f311d7 Iustin Pop
  except EnvironmentError, err:
1021 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
1022 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
1023 d9f311d7 Iustin Pop
    return 0
1024 fee80e90 Guido Trotter
1025 fee80e90 Guido Trotter
  try:
1026 13998ef2 Michael Hanselmann
    pid = int(raw_data)
1027 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
1028 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
1029 d9f311d7 Iustin Pop
    return 0
1030 fee80e90 Guido Trotter
1031 d9f311d7 Iustin Pop
  return pid
1032 fee80e90 Guido Trotter
1033 fee80e90 Guido Trotter
1034 debed9ae Michael Hanselmann
def ReadLockedPidFile(path):
1035 debed9ae Michael Hanselmann
  """Reads a locked PID file.
1036 debed9ae Michael Hanselmann

1037 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
1038 debed9ae Michael Hanselmann

1039 debed9ae Michael Hanselmann
  @type path: string
1040 debed9ae Michael Hanselmann
  @param path: Path to PID file
1041 debed9ae Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
1042 debed9ae Michael Hanselmann

1043 debed9ae Michael Hanselmann
  """
1044 debed9ae Michael Hanselmann
  try:
1045 debed9ae Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
1046 debed9ae Michael Hanselmann
  except EnvironmentError, err:
1047 debed9ae Michael Hanselmann
    if err.errno == errno.ENOENT:
1048 debed9ae Michael Hanselmann
      # PID file doesn't exist
1049 debed9ae Michael Hanselmann
      return None
1050 debed9ae Michael Hanselmann
    raise
1051 debed9ae Michael Hanselmann
1052 debed9ae Michael Hanselmann
  try:
1053 debed9ae Michael Hanselmann
    try:
1054 debed9ae Michael Hanselmann
      # Try to acquire lock
1055 debed9ae Michael Hanselmann
      LockFile(fd)
1056 debed9ae Michael Hanselmann
    except errors.LockError:
1057 debed9ae Michael Hanselmann
      # Couldn't lock, daemon is running
1058 debed9ae Michael Hanselmann
      return int(os.read(fd, 100))
1059 debed9ae Michael Hanselmann
  finally:
1060 debed9ae Michael Hanselmann
    os.close(fd)
1061 debed9ae Michael Hanselmann
1062 debed9ae Michael Hanselmann
  return None
1063 debed9ae Michael Hanselmann
1064 debed9ae Michael Hanselmann
1065 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
1066 a8083063 Iustin Pop
  """Try to match a name against a list.
1067 a8083063 Iustin Pop

1068 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
1069 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
1070 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
1071 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
1072 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
1073 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
1074 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
1075 a8083063 Iustin Pop

1076 58885d79 Iustin Pop
  @type key: str
1077 58885d79 Iustin Pop
  @param key: the name to be searched
1078 58885d79 Iustin Pop
  @type name_list: list
1079 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
1080 256eb94b Guido Trotter
  @type case_sensitive: boolean
1081 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
1082 a8083063 Iustin Pop

1083 58885d79 Iustin Pop
  @rtype: None or str
1084 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
1085 58885d79 Iustin Pop
      otherwise the element from the list which matches
1086 a8083063 Iustin Pop

1087 a8083063 Iustin Pop
  """
1088 3a541d90 Iustin Pop
  if key in name_list:
1089 3a541d90 Iustin Pop
    return key
1090 256eb94b Guido Trotter
1091 256eb94b Guido Trotter
  re_flags = 0
1092 256eb94b Guido Trotter
  if not case_sensitive:
1093 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
1094 099c52ad Iustin Pop
    key = key.upper()
1095 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
1096 256eb94b Guido Trotter
  names_filtered = []
1097 256eb94b Guido Trotter
  string_matches = []
1098 256eb94b Guido Trotter
  for name in name_list:
1099 256eb94b Guido Trotter
    if mo.match(name) is not None:
1100 256eb94b Guido Trotter
      names_filtered.append(name)
1101 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
1102 256eb94b Guido Trotter
        string_matches.append(name)
1103 256eb94b Guido Trotter
1104 256eb94b Guido Trotter
  if len(string_matches) == 1:
1105 256eb94b Guido Trotter
    return string_matches[0]
1106 256eb94b Guido Trotter
  if len(names_filtered) == 1:
1107 256eb94b Guido Trotter
    return names_filtered[0]
1108 256eb94b Guido Trotter
  return None
1109 a8083063 Iustin Pop
1110 a8083063 Iustin Pop
1111 28f34048 Michael Hanselmann
def ValidateServiceName(name):
1112 28f34048 Michael Hanselmann
  """Validate the given service name.
1113 28f34048 Michael Hanselmann

1114 28f34048 Michael Hanselmann
  @type name: number or string
1115 28f34048 Michael Hanselmann
  @param name: Service name or port specification
1116 28f34048 Michael Hanselmann

1117 28f34048 Michael Hanselmann
  """
1118 28f34048 Michael Hanselmann
  try:
1119 28f34048 Michael Hanselmann
    numport = int(name)
1120 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
1121 28f34048 Michael Hanselmann
    # Non-numeric service name
1122 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
1123 28f34048 Michael Hanselmann
  else:
1124 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
1125 28f34048 Michael Hanselmann
    # here)
1126 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
1127 28f34048 Michael Hanselmann
1128 28f34048 Michael Hanselmann
  if not valid:
1129 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
1130 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
1131 28f34048 Michael Hanselmann
1132 28f34048 Michael Hanselmann
  return name
1133 28f34048 Michael Hanselmann
1134 28f34048 Michael Hanselmann
1135 a8083063 Iustin Pop
def ListVolumeGroups():
1136 a8083063 Iustin Pop
  """List volume groups and their size
1137 a8083063 Iustin Pop

1138 58885d79 Iustin Pop
  @rtype: dict
1139 58885d79 Iustin Pop
  @return:
1140 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1141 58885d79 Iustin Pop
       the size of the volume
1142 a8083063 Iustin Pop

1143 a8083063 Iustin Pop
  """
1144 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
1145 a8083063 Iustin Pop
  result = RunCmd(command)
1146 a8083063 Iustin Pop
  retval = {}
1147 a8083063 Iustin Pop
  if result.failed:
1148 a8083063 Iustin Pop
    return retval
1149 a8083063 Iustin Pop
1150 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
1151 a8083063 Iustin Pop
    try:
1152 a8083063 Iustin Pop
      name, size = line.split()
1153 a8083063 Iustin Pop
      size = int(float(size))
1154 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
1155 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
1156 a8083063 Iustin Pop
      continue
1157 a8083063 Iustin Pop
1158 a8083063 Iustin Pop
    retval[name] = size
1159 a8083063 Iustin Pop
1160 a8083063 Iustin Pop
  return retval
1161 a8083063 Iustin Pop
1162 a8083063 Iustin Pop
1163 a8083063 Iustin Pop
def BridgeExists(bridge):
1164 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
1165 a8083063 Iustin Pop

1166 58885d79 Iustin Pop
  @type bridge: str
1167 58885d79 Iustin Pop
  @param bridge: the bridge name to check
1168 58885d79 Iustin Pop
  @rtype: boolean
1169 58885d79 Iustin Pop
  @return: True if it does
1170 a8083063 Iustin Pop

1171 a8083063 Iustin Pop
  """
1172 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
1173 a8083063 Iustin Pop
1174 a8083063 Iustin Pop
1175 a8083063 Iustin Pop
def NiceSort(name_list):
1176 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
1177 a8083063 Iustin Pop

1178 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
1179 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
1180 58885d79 Iustin Pop
  'a11']}.
1181 a8083063 Iustin Pop

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

1186 58885d79 Iustin Pop
  @type name_list: list
1187 58885d79 Iustin Pop
  @param name_list: the names to be sorted
1188 58885d79 Iustin Pop
  @rtype: list
1189 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
1190 a8083063 Iustin Pop

1191 a8083063 Iustin Pop
  """
1192 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
1193 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
1194 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1195 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1196 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
1197 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
1198 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
1199 a8083063 Iustin Pop
  def _TryInt(val):
1200 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
1201 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
1202 a8083063 Iustin Pop
      return val
1203 a8083063 Iustin Pop
    rval = int(val)
1204 a8083063 Iustin Pop
    return rval
1205 a8083063 Iustin Pop
1206 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
1207 a8083063 Iustin Pop
             for name in name_list]
1208 a8083063 Iustin Pop
  to_sort.sort()
1209 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
1210 a8083063 Iustin Pop
1211 a8083063 Iustin Pop
1212 a8083063 Iustin Pop
def TryConvert(fn, val):
1213 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
1214 a8083063 Iustin Pop

1215 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
1216 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
1217 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
1218 58885d79 Iustin Pop
  exceptions are propagated to the caller.
1219 58885d79 Iustin Pop

1220 58885d79 Iustin Pop
  @type fn: callable
1221 58885d79 Iustin Pop
  @param fn: function to apply to the value
1222 58885d79 Iustin Pop
  @param val: the value to be converted
1223 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
1224 58885d79 Iustin Pop
      otherwise the original value.
1225 a8083063 Iustin Pop

1226 a8083063 Iustin Pop
  """
1227 a8083063 Iustin Pop
  try:
1228 a8083063 Iustin Pop
    nv = fn(val)
1229 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
1230 a8083063 Iustin Pop
    nv = val
1231 a8083063 Iustin Pop
  return nv
1232 a8083063 Iustin Pop
1233 a8083063 Iustin Pop
1234 a8083063 Iustin Pop
def IsValidShellParam(word):
1235 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1236 a8083063 Iustin Pop

1237 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1238 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1239 a8083063 Iustin Pop
  the actual command.
1240 a8083063 Iustin Pop

1241 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1242 a8083063 Iustin Pop
  side.
1243 a8083063 Iustin Pop

1244 58885d79 Iustin Pop
  @type word: str
1245 58885d79 Iustin Pop
  @param word: the word to check
1246 58885d79 Iustin Pop
  @rtype: boolean
1247 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1248 58885d79 Iustin Pop

1249 a8083063 Iustin Pop
  """
1250 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
1251 a8083063 Iustin Pop
1252 a8083063 Iustin Pop
1253 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1254 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1255 a8083063 Iustin Pop

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

1261 58885d79 Iustin Pop
  @type template: str
1262 58885d79 Iustin Pop
  @param template: the string holding the template for the
1263 58885d79 Iustin Pop
      string formatting
1264 58885d79 Iustin Pop
  @rtype: str
1265 58885d79 Iustin Pop
  @return: the expanded command line
1266 58885d79 Iustin Pop

1267 a8083063 Iustin Pop
  """
1268 a8083063 Iustin Pop
  for word in args:
1269 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1270 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1271 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1272 a8083063 Iustin Pop
  return template % args
1273 a8083063 Iustin Pop
1274 a8083063 Iustin Pop
1275 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
1276 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
1277 a8083063 Iustin Pop

1278 58885d79 Iustin Pop
  @type value: int
1279 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
1280 9fbfbb7b Iustin Pop
  @type units: char
1281 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
1282 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
1283 9fbfbb7b Iustin Pop
      - 'm' for MiBs
1284 9fbfbb7b Iustin Pop
      - 'g' for GiBs
1285 9fbfbb7b Iustin Pop
      - 't' for TiBs
1286 58885d79 Iustin Pop
  @rtype: str
1287 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
1288 a8083063 Iustin Pop

1289 a8083063 Iustin Pop
  """
1290 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
1291 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
1292 a8083063 Iustin Pop
1293 9fbfbb7b Iustin Pop
  suffix = ''
1294 9fbfbb7b Iustin Pop
1295 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
1296 9fbfbb7b Iustin Pop
    if units == 'h':
1297 9fbfbb7b Iustin Pop
      suffix = 'M'
1298 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
1299 9fbfbb7b Iustin Pop
1300 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
1301 9fbfbb7b Iustin Pop
    if units == 'h':
1302 9fbfbb7b Iustin Pop
      suffix = 'G'
1303 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
1304 a8083063 Iustin Pop
1305 a8083063 Iustin Pop
  else:
1306 9fbfbb7b Iustin Pop
    if units == 'h':
1307 9fbfbb7b Iustin Pop
      suffix = 'T'
1308 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
1309 a8083063 Iustin Pop
1310 a8083063 Iustin Pop
1311 a8083063 Iustin Pop
def ParseUnit(input_string):
1312 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
1313 a8083063 Iustin Pop

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

1318 a8083063 Iustin Pop
  """
1319 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
1320 a8083063 Iustin Pop
  if not m:
1321 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
1322 a8083063 Iustin Pop
1323 a8083063 Iustin Pop
  value = float(m.groups()[0])
1324 a8083063 Iustin Pop
1325 a8083063 Iustin Pop
  unit = m.groups()[1]
1326 a8083063 Iustin Pop
  if unit:
1327 a8083063 Iustin Pop
    lcunit = unit.lower()
1328 a8083063 Iustin Pop
  else:
1329 a8083063 Iustin Pop
    lcunit = 'm'
1330 a8083063 Iustin Pop
1331 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
1332 a8083063 Iustin Pop
    # Value already in MiB
1333 a8083063 Iustin Pop
    pass
1334 a8083063 Iustin Pop
1335 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
1336 a8083063 Iustin Pop
    value *= 1024
1337 a8083063 Iustin Pop
1338 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
1339 a8083063 Iustin Pop
    value *= 1024 * 1024
1340 a8083063 Iustin Pop
1341 a8083063 Iustin Pop
  else:
1342 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
1343 a8083063 Iustin Pop
1344 a8083063 Iustin Pop
  # Make sure we round up
1345 a8083063 Iustin Pop
  if int(value) < value:
1346 a8083063 Iustin Pop
    value += 1
1347 a8083063 Iustin Pop
1348 a8083063 Iustin Pop
  # Round up to the next multiple of 4
1349 a8083063 Iustin Pop
  value = int(value)
1350 a8083063 Iustin Pop
  if value % 4:
1351 a8083063 Iustin Pop
    value += 4 - value % 4
1352 a8083063 Iustin Pop
1353 a8083063 Iustin Pop
  return value
1354 a8083063 Iustin Pop
1355 a8083063 Iustin Pop
1356 31155d60 Balazs Lecz
def ParseCpuMask(cpu_mask):
1357 31155d60 Balazs Lecz
  """Parse a CPU mask definition and return the list of CPU IDs.
1358 31155d60 Balazs Lecz

1359 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
1360 31155d60 Balazs Lecz
  or dash-separated ID ranges
1361 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
1362 31155d60 Balazs Lecz

1363 31155d60 Balazs Lecz
  @type cpu_mask: str
1364 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
1365 31155d60 Balazs Lecz
  @rtype: list of int
1366 31155d60 Balazs Lecz
  @return: list of CPU IDs
1367 31155d60 Balazs Lecz

1368 31155d60 Balazs Lecz
  """
1369 31155d60 Balazs Lecz
  if not cpu_mask:
1370 31155d60 Balazs Lecz
    return []
1371 31155d60 Balazs Lecz
  cpu_list = []
1372 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
1373 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
1374 31155d60 Balazs Lecz
    n_elements = len(boundaries)
1375 31155d60 Balazs Lecz
    if n_elements > 2:
1376 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1377 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
1378 31155d60 Balazs Lecz
    try:
1379 31155d60 Balazs Lecz
      lower = int(boundaries[0])
1380 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1381 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
1382 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1383 31155d60 Balazs Lecz
    try:
1384 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
1385 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
1386 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
1387 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
1388 31155d60 Balazs Lecz
    if lower > higher:
1389 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
1390 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
1391 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
1392 31155d60 Balazs Lecz
  return cpu_list
1393 31155d60 Balazs Lecz
1394 31155d60 Balazs Lecz
1395 3727671e Renรฉ Nussbaumer
def AddAuthorizedKey(file_obj, key):
1396 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1397 a8083063 Iustin Pop

1398 3727671e Renรฉ Nussbaumer
  @type file_obj: str or file handle
1399 3727671e Renรฉ Nussbaumer
  @param file_obj: path to authorized_keys file
1400 58885d79 Iustin Pop
  @type key: str
1401 58885d79 Iustin Pop
  @param key: string containing key
1402 58885d79 Iustin Pop

1403 a8083063 Iustin Pop
  """
1404 a8083063 Iustin Pop
  key_fields = key.split()
1405 a8083063 Iustin Pop
1406 3727671e Renรฉ Nussbaumer
  if isinstance(file_obj, basestring):
1407 3727671e Renรฉ Nussbaumer
    f = open(file_obj, 'a+')
1408 3727671e Renรฉ Nussbaumer
  else:
1409 3727671e Renรฉ Nussbaumer
    f = file_obj
1410 3727671e Renรฉ Nussbaumer
1411 a8083063 Iustin Pop
  try:
1412 a8083063 Iustin Pop
    nl = True
1413 a8083063 Iustin Pop
    for line in f:
1414 a8083063 Iustin Pop
      # Ignore whitespace changes
1415 a8083063 Iustin Pop
      if line.split() == key_fields:
1416 a8083063 Iustin Pop
        break
1417 a8083063 Iustin Pop
      nl = line.endswith('\n')
1418 a8083063 Iustin Pop
    else:
1419 a8083063 Iustin Pop
      if not nl:
1420 a8083063 Iustin Pop
        f.write("\n")
1421 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1422 a8083063 Iustin Pop
      f.write("\n")
1423 a8083063 Iustin Pop
      f.flush()
1424 a8083063 Iustin Pop
  finally:
1425 a8083063 Iustin Pop
    f.close()
1426 a8083063 Iustin Pop
1427 a8083063 Iustin Pop
1428 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1429 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1430 a8083063 Iustin Pop

1431 58885d79 Iustin Pop
  @type file_name: str
1432 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1433 58885d79 Iustin Pop
  @type key: str
1434 58885d79 Iustin Pop
  @param key: string containing key
1435 58885d79 Iustin Pop

1436 a8083063 Iustin Pop
  """
1437 a8083063 Iustin Pop
  key_fields = key.split()
1438 a8083063 Iustin Pop
1439 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1440 a8083063 Iustin Pop
  try:
1441 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1442 a8083063 Iustin Pop
    try:
1443 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1444 59f82e3f Michael Hanselmann
      try:
1445 59f82e3f Michael Hanselmann
        for line in f:
1446 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1447 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1448 59f82e3f Michael Hanselmann
            out.write(line)
1449 899d2a81 Michael Hanselmann
1450 899d2a81 Michael Hanselmann
        out.flush()
1451 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1452 899d2a81 Michael Hanselmann
      finally:
1453 899d2a81 Michael Hanselmann
        f.close()
1454 899d2a81 Michael Hanselmann
    finally:
1455 899d2a81 Michael Hanselmann
      out.close()
1456 899d2a81 Michael Hanselmann
  except:
1457 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1458 899d2a81 Michael Hanselmann
    raise
1459 899d2a81 Michael Hanselmann
1460 899d2a81 Michael Hanselmann
1461 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1462 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1463 899d2a81 Michael Hanselmann

1464 58885d79 Iustin Pop
  @type file_name: str
1465 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1466 58885d79 Iustin Pop
  @type ip: str
1467 58885d79 Iustin Pop
  @param ip: the IP address
1468 58885d79 Iustin Pop
  @type hostname: str
1469 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1470 58885d79 Iustin Pop
  @type aliases: list
1471 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1472 58885d79 Iustin Pop

1473 899d2a81 Michael Hanselmann
  """
1474 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1475 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1476 7fbb1f65 Michael Hanselmann
1477 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1478 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1479 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1480 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1481 9440aeab Michael Hanselmann
    try:
1482 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1483 edcd876b Michael Hanselmann
        fields = line.split()
1484 edcd876b Michael Hanselmann
        if fields and not fields[0].startswith("#") and ip == fields[0]:
1485 edcd876b Michael Hanselmann
          continue
1486 edcd876b Michael Hanselmann
        out.write(line)
1487 edcd876b Michael Hanselmann
1488 edcd876b Michael Hanselmann
      out.write("%s\t%s" % (ip, hostname))
1489 edcd876b Michael Hanselmann
      if aliases:
1490 edcd876b Michael Hanselmann
        out.write(" %s" % " ".join(aliases))
1491 edcd876b Michael Hanselmann
      out.write("\n")
1492 edcd876b Michael Hanselmann
      out.flush()
1493 9440aeab Michael Hanselmann
    finally:
1494 9440aeab Michael Hanselmann
      out.close()
1495 edcd876b Michael Hanselmann
1496 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1497 899d2a81 Michael Hanselmann
1498 899d2a81 Michael Hanselmann
1499 ea8ac9c9 Renรฉ Nussbaumer
def AddHostToEtcHosts(hostname, ip):
1500 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1501 d9c02ca6 Michael Hanselmann

1502 58885d79 Iustin Pop
  @type hostname: str
1503 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1504 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1505 ea8ac9c9 Renรฉ Nussbaumer
  @type ip: str
1506 ea8ac9c9 Renรฉ Nussbaumer
  @param ip: The ip address of the host
1507 58885d79 Iustin Pop

1508 d9c02ca6 Michael Hanselmann
  """
1509 ea8ac9c9 Renรฉ Nussbaumer
  SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]])
1510 d9c02ca6 Michael Hanselmann
1511 d9c02ca6 Michael Hanselmann
1512 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1513 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1514 899d2a81 Michael Hanselmann

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

1517 58885d79 Iustin Pop
  @type file_name: str
1518 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1519 58885d79 Iustin Pop
  @type hostname: str
1520 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1521 58885d79 Iustin Pop

1522 899d2a81 Michael Hanselmann
  """
1523 edcd876b Michael Hanselmann
  def _WriteEtcHosts(fd):
1524 edcd876b Michael Hanselmann
    # Duplicating file descriptor because os.fdopen's result will automatically
1525 edcd876b Michael Hanselmann
    # close the descriptor, but we would still like to have its functionality.
1526 edcd876b Michael Hanselmann
    out = os.fdopen(os.dup(fd), "w")
1527 899d2a81 Michael Hanselmann
    try:
1528 edcd876b Michael Hanselmann
      for line in ReadFile(file_name).splitlines(True):
1529 edcd876b Michael Hanselmann
        fields = line.split()
1530 edcd876b Michael Hanselmann
        if len(fields) > 1 and not fields[0].startswith("#"):
1531 edcd876b Michael Hanselmann
          names = fields[1:]
1532 edcd876b Michael Hanselmann
          if hostname in names:
1533 edcd876b Michael Hanselmann
            while hostname in names:
1534 edcd876b Michael Hanselmann
              names.remove(hostname)
1535 edcd876b Michael Hanselmann
            if names:
1536 edcd876b Michael Hanselmann
              out.write("%s %s\n" % (fields[0], " ".join(names)))
1537 edcd876b Michael Hanselmann
            continue
1538 59f82e3f Michael Hanselmann
1539 edcd876b Michael Hanselmann
        out.write(line)
1540 edcd876b Michael Hanselmann
1541 edcd876b Michael Hanselmann
      out.flush()
1542 a8083063 Iustin Pop
    finally:
1543 59f82e3f Michael Hanselmann
      out.close()
1544 edcd876b Michael Hanselmann
1545 edcd876b Michael Hanselmann
  WriteFile(file_name, fn=_WriteEtcHosts, mode=0644)
1546 a8083063 Iustin Pop
1547 a8083063 Iustin Pop
1548 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1549 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1550 d9c02ca6 Michael Hanselmann

1551 58885d79 Iustin Pop
  @type hostname: str
1552 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1553 58885d79 Iustin Pop
      full and shot name will be removed from
1554 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1555 58885d79 Iustin Pop

1556 d9c02ca6 Michael Hanselmann
  """
1557 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname)
1558 b705c7a6 Manuel Franceschini
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0])
1559 d9c02ca6 Michael Hanselmann
1560 d9c02ca6 Michael Hanselmann
1561 1d466a4f Michael Hanselmann
def TimestampForFilename():
1562 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1563 1d466a4f Michael Hanselmann

1564 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1565 1d466a4f Michael Hanselmann
  separators.
1566 1d466a4f Michael Hanselmann

1567 1d466a4f Michael Hanselmann
  """
1568 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1569 1d466a4f Michael Hanselmann
1570 1d466a4f Michael Hanselmann
1571 a8083063 Iustin Pop
def CreateBackup(file_name):
1572 a8083063 Iustin Pop
  """Creates a backup of a file.
1573 a8083063 Iustin Pop

1574 58885d79 Iustin Pop
  @type file_name: str
1575 58885d79 Iustin Pop
  @param file_name: file to be backed up
1576 58885d79 Iustin Pop
  @rtype: str
1577 58885d79 Iustin Pop
  @return: the path to the newly created backup
1578 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1579 a8083063 Iustin Pop

1580 a8083063 Iustin Pop
  """
1581 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1582 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1583 3ecf6786 Iustin Pop
                                file_name)
1584 a8083063 Iustin Pop
1585 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1586 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1587 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1588 081b1e69 Michael Hanselmann
1589 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1590 081b1e69 Michael Hanselmann
  try:
1591 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1592 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1593 081b1e69 Michael Hanselmann
    try:
1594 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1595 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1596 081b1e69 Michael Hanselmann
    finally:
1597 081b1e69 Michael Hanselmann
      fdst.close()
1598 081b1e69 Michael Hanselmann
  finally:
1599 081b1e69 Michael Hanselmann
    fsrc.close()
1600 081b1e69 Michael Hanselmann
1601 a8083063 Iustin Pop
  return backup_name
1602 a8083063 Iustin Pop
1603 a8083063 Iustin Pop
1604 a8083063 Iustin Pop
def ShellQuote(value):
1605 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1606 3ecf6786 Iustin Pop

1607 58885d79 Iustin Pop
  @type value: str
1608 58885d79 Iustin Pop
  @param value: the argument to be quoted
1609 58885d79 Iustin Pop
  @rtype: str
1610 58885d79 Iustin Pop
  @return: the quoted value
1611 58885d79 Iustin Pop

1612 a8083063 Iustin Pop
  """
1613 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1614 a8083063 Iustin Pop
    return value
1615 a8083063 Iustin Pop
  else:
1616 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1617 a8083063 Iustin Pop
1618 a8083063 Iustin Pop
1619 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1620 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1621 58885d79 Iustin Pop

1622 58885d79 Iustin Pop
  @type args: list
1623 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1624 58885d79 Iustin Pop
  @rtype: str
1625 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1626 a8083063 Iustin Pop

1627 a8083063 Iustin Pop
  """
1628 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1629 88d14415 Michael Hanselmann
1630 88d14415 Michael Hanselmann
1631 858905fb Michael Hanselmann
class ShellWriter:
1632 858905fb Michael Hanselmann
  """Helper class to write scripts with indentation.
1633 858905fb Michael Hanselmann

1634 858905fb Michael Hanselmann
  """
1635 858905fb Michael Hanselmann
  INDENT_STR = "  "
1636 858905fb Michael Hanselmann
1637 858905fb Michael Hanselmann
  def __init__(self, fh):
1638 858905fb Michael Hanselmann
    """Initializes this class.
1639 858905fb Michael Hanselmann

1640 858905fb Michael Hanselmann
    """
1641 858905fb Michael Hanselmann
    self._fh = fh
1642 858905fb Michael Hanselmann
    self._indent = 0
1643 858905fb Michael Hanselmann
1644 858905fb Michael Hanselmann
  def IncIndent(self):
1645 858905fb Michael Hanselmann
    """Increase indentation level by 1.
1646 858905fb Michael Hanselmann

1647 858905fb Michael Hanselmann
    """
1648 858905fb Michael Hanselmann
    self._indent += 1
1649 858905fb Michael Hanselmann
1650 858905fb Michael Hanselmann
  def DecIndent(self):
1651 858905fb Michael Hanselmann
    """Decrease indentation level by 1.
1652 858905fb Michael Hanselmann

1653 858905fb Michael Hanselmann
    """
1654 858905fb Michael Hanselmann
    assert self._indent > 0
1655 858905fb Michael Hanselmann
    self._indent -= 1
1656 858905fb Michael Hanselmann
1657 858905fb Michael Hanselmann
  def Write(self, txt, *args):
1658 858905fb Michael Hanselmann
    """Write line to output file.
1659 858905fb Michael Hanselmann

1660 858905fb Michael Hanselmann
    """
1661 858905fb Michael Hanselmann
    assert self._indent >= 0
1662 858905fb Michael Hanselmann
1663 858905fb Michael Hanselmann
    self._fh.write(self._indent * self.INDENT_STR)
1664 858905fb Michael Hanselmann
1665 858905fb Michael Hanselmann
    if args:
1666 858905fb Michael Hanselmann
      self._fh.write(txt % args)
1667 858905fb Michael Hanselmann
    else:
1668 858905fb Michael Hanselmann
      self._fh.write(txt)
1669 858905fb Michael Hanselmann
1670 858905fb Michael Hanselmann
    self._fh.write("\n")
1671 858905fb Michael Hanselmann
1672 858905fb Michael Hanselmann
1673 b5b8309d Guido Trotter
def ListVisibleFiles(path):
1674 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1675 58885d79 Iustin Pop

1676 58885d79 Iustin Pop
  @type path: str
1677 58885d79 Iustin Pop
  @param path: the directory to enumerate
1678 58885d79 Iustin Pop
  @rtype: list
1679 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1680 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1681 eedbda4b Michael Hanselmann

1682 eedbda4b Michael Hanselmann
  """
1683 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1684 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1685 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1686 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1687 f3299a07 Michael Hanselmann
  return files
1688 2f8b60b3 Iustin Pop
1689 2f8b60b3 Iustin Pop
1690 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1691 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1692 257f4c0a Iustin Pop

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

1697 2f8b60b3 Iustin Pop
  """
1698 2f8b60b3 Iustin Pop
  try:
1699 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1700 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1701 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1702 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1703 257f4c0a Iustin Pop
    else:
1704 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1705 257f4c0a Iustin Pop
                                   type(user))
1706 2f8b60b3 Iustin Pop
  except KeyError:
1707 2f8b60b3 Iustin Pop
    return default
1708 2f8b60b3 Iustin Pop
  return result.pw_dir
1709 59072e7e Michael Hanselmann
1710 59072e7e Michael Hanselmann
1711 24818e8f Michael Hanselmann
def NewUUID():
1712 59072e7e Michael Hanselmann
  """Returns a random UUID.
1713 59072e7e Michael Hanselmann

1714 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1715 58885d79 Iustin Pop
      filesystem.
1716 58885d79 Iustin Pop
  @rtype: str
1717 58885d79 Iustin Pop

1718 59072e7e Michael Hanselmann
  """
1719 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1720 087b34fe Iustin Pop
1721 087b34fe Iustin Pop
1722 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1723 33081d90 Iustin Pop
  """Generates a random secret.
1724 33081d90 Iustin Pop

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

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

1733 33081d90 Iustin Pop
  """
1734 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1735 33081d90 Iustin Pop
1736 33081d90 Iustin Pop
1737 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1738 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1739 9dae41ad Guido Trotter

1740 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1741 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1742 9dae41ad Guido Trotter

1743 9dae41ad Guido Trotter
  """
1744 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1745 9dae41ad Guido Trotter
    try:
1746 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1747 9dae41ad Guido Trotter
    except EnvironmentError, err:
1748 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1749 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1750 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1751 b73360e3 Balazs Lecz
    try:
1752 b73360e3 Balazs Lecz
      os.chmod(dir_name, dir_mode)
1753 b73360e3 Balazs Lecz
    except EnvironmentError, err:
1754 b73360e3 Balazs Lecz
      raise errors.GenericError("Cannot change directory permissions on"
1755 b73360e3 Balazs Lecz
                                " '%s': %s" % (dir_name, err))
1756 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1757 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1758 9dae41ad Guido Trotter
1759 9dae41ad Guido Trotter
1760 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1761 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1762 ca0aa6d0 Michael Hanselmann

1763 016308cb Iustin Pop
  @type size: int
1764 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1765 58885d79 Iustin Pop
  @rtype: str
1766 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1767 ca0aa6d0 Michael Hanselmann

1768 ca0aa6d0 Michael Hanselmann
  """
1769 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1770 ca0aa6d0 Michael Hanselmann
  try:
1771 582ed043 Guido Trotter
    return f.read(size)
1772 ca0aa6d0 Michael Hanselmann
  finally:
1773 ca0aa6d0 Michael Hanselmann
    f.close()
1774 ca0aa6d0 Michael Hanselmann
1775 ca0aa6d0 Michael Hanselmann
1776 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1777 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1778 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1779 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1780 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1781 087b34fe Iustin Pop
  """(Over)write a file atomically.
1782 087b34fe Iustin Pop

1783 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1784 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1785 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1786 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1787 087b34fe Iustin Pop
  mtime/atime of the file.
1788 087b34fe Iustin Pop

1789 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1790 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1791 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1792 087b34fe Iustin Pop
  temporary file should be removed.
1793 087b34fe Iustin Pop

1794 58885d79 Iustin Pop
  @type file_name: str
1795 58885d79 Iustin Pop
  @param file_name: the target filename
1796 58885d79 Iustin Pop
  @type fn: callable
1797 58885d79 Iustin Pop
  @param fn: content writing function, called with
1798 58885d79 Iustin Pop
      file descriptor as parameter
1799 69efe319 Michael Hanselmann
  @type data: str
1800 58885d79 Iustin Pop
  @param data: contents of the file
1801 58885d79 Iustin Pop
  @type mode: int
1802 58885d79 Iustin Pop
  @param mode: file mode
1803 58885d79 Iustin Pop
  @type uid: int
1804 58885d79 Iustin Pop
  @param uid: the owner of the file
1805 58885d79 Iustin Pop
  @type gid: int
1806 58885d79 Iustin Pop
  @param gid: the group of the file
1807 58885d79 Iustin Pop
  @type atime: int
1808 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1809 58885d79 Iustin Pop
  @type mtime: int
1810 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1811 58885d79 Iustin Pop
  @type close: boolean
1812 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1813 58885d79 Iustin Pop
  @type prewrite: callable
1814 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1815 58885d79 Iustin Pop
  @type postwrite: callable
1816 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1817 58885d79 Iustin Pop

1818 58885d79 Iustin Pop
  @rtype: None or int
1819 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1820 58885d79 Iustin Pop
      otherwise the file descriptor
1821 58885d79 Iustin Pop

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

1824 087b34fe Iustin Pop
  """
1825 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1826 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1827 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1828 087b34fe Iustin Pop
1829 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1830 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1831 087b34fe Iustin Pop
1832 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1833 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1834 087b34fe Iustin Pop
                                 " set or None")
1835 087b34fe Iustin Pop
1836 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1837 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1838 087b34fe Iustin Pop
1839 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1840 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1841 81b7354c Iustin Pop
  do_remove = True
1842 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1843 087b34fe Iustin Pop
  # leaves it in place
1844 087b34fe Iustin Pop
  try:
1845 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1846 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1847 087b34fe Iustin Pop
    if mode:
1848 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1849 71714516 Michael Hanselmann
    if callable(prewrite):
1850 71714516 Michael Hanselmann
      prewrite(fd)
1851 087b34fe Iustin Pop
    if data is not None:
1852 087b34fe Iustin Pop
      os.write(fd, data)
1853 087b34fe Iustin Pop
    else:
1854 087b34fe Iustin Pop
      fn(fd)
1855 71714516 Michael Hanselmann
    if callable(postwrite):
1856 71714516 Michael Hanselmann
      postwrite(fd)
1857 087b34fe Iustin Pop
    os.fsync(fd)
1858 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1859 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1860 70f4497c Michael Hanselmann
    if not dry_run:
1861 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1862 81b7354c Iustin Pop
      do_remove = False
1863 087b34fe Iustin Pop
  finally:
1864 71714516 Michael Hanselmann
    if close:
1865 71714516 Michael Hanselmann
      os.close(fd)
1866 71714516 Michael Hanselmann
      result = None
1867 71714516 Michael Hanselmann
    else:
1868 71714516 Michael Hanselmann
      result = fd
1869 81b7354c Iustin Pop
    if do_remove:
1870 81b7354c Iustin Pop
      RemoveFile(new_name)
1871 78feb6fb Guido Trotter
1872 71714516 Michael Hanselmann
  return result
1873 71714516 Michael Hanselmann
1874 78feb6fb Guido Trotter
1875 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
1876 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
1877 e587b46a Guido Trotter

1878 e587b46a Guido Trotter
  @type strict: boolean
1879 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1880 e587b46a Guido Trotter
      non-empty line
1881 e587b46a Guido Trotter

1882 e587b46a Guido Trotter
  """
1883 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1884 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1885 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1886 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1887 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1888 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1889 e587b46a Guido Trotter
                              file_name)
1890 e587b46a Guido Trotter
  return full_lines[0]
1891 e587b46a Guido Trotter
1892 e587b46a Guido Trotter
1893 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1894 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1895 7b4126b7 Iustin Pop

1896 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1897 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1898 7b4126b7 Iustin Pop
  value, the index will be returned.
1899 7b4126b7 Iustin Pop

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

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

1905 58885d79 Iustin Pop
  @type seq: sequence
1906 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1907 58885d79 Iustin Pop
  @type base: int
1908 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1909 58885d79 Iustin Pop
  @rtype: int
1910 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1911 7b4126b7 Iustin Pop

1912 7b4126b7 Iustin Pop
  """
1913 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1914 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1915 7b4126b7 Iustin Pop
    if elem > idx + base:
1916 7b4126b7 Iustin Pop
      # idx is not used
1917 7b4126b7 Iustin Pop
      return idx + base
1918 7b4126b7 Iustin Pop
  return None
1919 7b4126b7 Iustin Pop
1920 7b4126b7 Iustin Pop
1921 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1922 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1923 dcd511c8 Guido Trotter

1924 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1925 dfdc4060 Guido Trotter

1926 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1927 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1928 dfdc4060 Guido Trotter
  @type event: integer
1929 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1930 dcd511c8 Guido Trotter
  @type timeout: float or None
1931 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1932 dcd511c8 Guido Trotter
  @rtype: int or None
1933 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1934 dcd511c8 Guido Trotter

1935 dcd511c8 Guido Trotter
  """
1936 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1937 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1938 dcd511c8 Guido Trotter
1939 dcd511c8 Guido Trotter
  if timeout is not None:
1940 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1941 dcd511c8 Guido Trotter
    timeout *= 1000
1942 dcd511c8 Guido Trotter
1943 dcd511c8 Guido Trotter
  poller = select.poll()
1944 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1945 dcd511c8 Guido Trotter
  try:
1946 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1947 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1948 dfdc4060 Guido Trotter
    # every so often.
1949 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1950 dfdc4060 Guido Trotter
  except select.error, err:
1951 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1952 dfdc4060 Guido Trotter
      raise
1953 dfdc4060 Guido Trotter
    io_events = []
1954 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1955 dfdc4060 Guido Trotter
    return io_events[0][1]
1956 dfdc4060 Guido Trotter
  else:
1957 dfdc4060 Guido Trotter
    return None
1958 dfdc4060 Guido Trotter
1959 dfdc4060 Guido Trotter
1960 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1961 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1962 dfdc4060 Guido Trotter

1963 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1964 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1965 dfdc4060 Guido Trotter
  expired.
1966 dfdc4060 Guido Trotter

1967 dfdc4060 Guido Trotter
  """
1968 dfdc4060 Guido Trotter
1969 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1970 dfdc4060 Guido Trotter
    self.timeout = timeout
1971 dfdc4060 Guido Trotter
1972 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1973 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1974 dfdc4060 Guido Trotter
    if result is None:
1975 dfdc4060 Guido Trotter
      raise RetryAgain()
1976 dfdc4060 Guido Trotter
    else:
1977 dfdc4060 Guido Trotter
      return result
1978 dfdc4060 Guido Trotter
1979 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1980 dfdc4060 Guido Trotter
    self.timeout = timeout
1981 dfdc4060 Guido Trotter
1982 dfdc4060 Guido Trotter
1983 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1984 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1985 dfdc4060 Guido Trotter

1986 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1987 dfdc4060 Guido Trotter

1988 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1989 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1990 dfdc4060 Guido Trotter
  @type event: integer
1991 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1992 dfdc4060 Guido Trotter
  @type timeout: float or None
1993 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1994 dfdc4060 Guido Trotter
  @rtype: int or None
1995 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1996 dfdc4060 Guido Trotter

1997 dfdc4060 Guido Trotter
  """
1998 dfdc4060 Guido Trotter
  if timeout is not None:
1999 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
2000 1b429e2a Iustin Pop
    try:
2001 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
2002 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
2003 1b429e2a Iustin Pop
    except RetryTimeout:
2004 1b429e2a Iustin Pop
      result = None
2005 dfdc4060 Guido Trotter
  else:
2006 dfdc4060 Guido Trotter
    result = None
2007 dfdc4060 Guido Trotter
    while result is None:
2008 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
2009 dfdc4060 Guido Trotter
  return result
2010 2de64672 Iustin Pop
2011 2de64672 Iustin Pop
2012 f7414041 Michael Hanselmann
def UniqueSequence(seq):
2013 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
2014 f7414041 Michael Hanselmann

2015 f7414041 Michael Hanselmann
  Element order is preserved.
2016 58885d79 Iustin Pop

2017 58885d79 Iustin Pop
  @type seq: sequence
2018 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
2019 58885d79 Iustin Pop
  @rtype: list
2020 58885d79 Iustin Pop
  @return: list of unique elements from seq
2021 58885d79 Iustin Pop

2022 f7414041 Michael Hanselmann
  """
2023 f7414041 Michael Hanselmann
  seen = set()
2024 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
2025 1862d460 Alexander Schreiber
2026 1862d460 Alexander Schreiber
2027 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
2028 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
2029 1862d460 Alexander Schreiber

2030 5bbd3f7f Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
2031 82187135 Renรฉ Nussbaumer
  accepts colon separated format. Normalize it to all lower.
2032 58885d79 Iustin Pop

2033 58885d79 Iustin Pop
  @type mac: str
2034 58885d79 Iustin Pop
  @param mac: the MAC to be validated
2035 82187135 Renรฉ Nussbaumer
  @rtype: str
2036 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
2037 82187135 Renรฉ Nussbaumer

2038 82187135 Renรฉ Nussbaumer
  @raise errors.OpPrereqError: If the MAC isn't valid
2039 58885d79 Iustin Pop

2040 1862d460 Alexander Schreiber
  """
2041 8fb00704 Iustin Pop
  if not _MAC_CHECK.match(mac):
2042 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
2043 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
2044 82187135 Renรฉ Nussbaumer
2045 82187135 Renรฉ Nussbaumer
  return mac.lower()
2046 06009e27 Iustin Pop
2047 06009e27 Iustin Pop
2048 06009e27 Iustin Pop
def TestDelay(duration):
2049 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
2050 06009e27 Iustin Pop

2051 58885d79 Iustin Pop
  @type duration: float
2052 58885d79 Iustin Pop
  @param duration: the sleep duration
2053 58885d79 Iustin Pop
  @rtype: boolean
2054 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
2055 58885d79 Iustin Pop

2056 06009e27 Iustin Pop
  """
2057 06009e27 Iustin Pop
  if duration < 0:
2058 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
2059 06009e27 Iustin Pop
  time.sleep(duration)
2060 38ea42a1 Iustin Pop
  return True, None
2061 8f765069 Iustin Pop
2062 8f765069 Iustin Pop
2063 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
2064 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
2065 8f765069 Iustin Pop

2066 7d88772a Iustin Pop
  @type fd: int
2067 7d88772a Iustin Pop
  @param fd: the file descriptor
2068 7d88772a Iustin Pop
  @type retries: int
2069 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
2070 7d88772a Iustin Pop
      other error than EBADF
2071 7d88772a Iustin Pop

2072 7d88772a Iustin Pop
  """
2073 7d88772a Iustin Pop
  try:
2074 7d88772a Iustin Pop
    os.close(fd)
2075 7d88772a Iustin Pop
  except OSError, err:
2076 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
2077 7d88772a Iustin Pop
      if retries > 0:
2078 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
2079 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
2080 7d88772a Iustin Pop
    # ignore this and go on
2081 7d88772a Iustin Pop
2082 7d88772a Iustin Pop
2083 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
2084 7d88772a Iustin Pop
  """Close file descriptors.
2085 7d88772a Iustin Pop

2086 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
2087 7d88772a Iustin Pop
  stdin/out/err).
2088 8f765069 Iustin Pop

2089 58885d79 Iustin Pop
  @type noclose_fds: list or None
2090 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
2091 58885d79 Iustin Pop
      that should not be closed
2092 58885d79 Iustin Pop

2093 8f765069 Iustin Pop
  """
2094 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
2095 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
2096 8f765069 Iustin Pop
    try:
2097 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
2098 8f765069 Iustin Pop
      if MAXFD < 0:
2099 8f765069 Iustin Pop
        MAXFD = 1024
2100 8f765069 Iustin Pop
    except OSError:
2101 8f765069 Iustin Pop
      MAXFD = 1024
2102 8f765069 Iustin Pop
  else:
2103 8f765069 Iustin Pop
    MAXFD = 1024
2104 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
2105 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
2106 7d88772a Iustin Pop
    maxfd = MAXFD
2107 7d88772a Iustin Pop
2108 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
2109 7d88772a Iustin Pop
  for fd in range(3, maxfd):
2110 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
2111 7d88772a Iustin Pop
      continue
2112 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2113 7d88772a Iustin Pop
2114 7d88772a Iustin Pop
2115 4c32a8bd Luca Bigliardi
def Mlockall(_ctypes=ctypes):
2116 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
2117 4b6fa0bf Luca Bigliardi

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

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

2123 4b6fa0bf Luca Bigliardi
  """
2124 4c32a8bd Luca Bigliardi
  if _ctypes is None:
2125 4c32a8bd Luca Bigliardi
    raise errors.NoCtypesError()
2126 4b6fa0bf Luca Bigliardi
2127 4c32a8bd Luca Bigliardi
  libc = _ctypes.cdll.LoadLibrary("libc.so.6")
2128 4b6fa0bf Luca Bigliardi
  if libc is None:
2129 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
2130 4b6fa0bf Luca Bigliardi
    return
2131 4b6fa0bf Luca Bigliardi
2132 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
2133 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
2134 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
2135 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
2136 4b6fa0bf Luca Bigliardi
  # the actual error code was.
2137 20601361 Luca Bigliardi
  # pylint: disable-msg=W0212
2138 4c32a8bd Luca Bigliardi
  libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
2139 4b6fa0bf Luca Bigliardi
2140 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
2141 20601361 Luca Bigliardi
    # pylint: disable-msg=W0212
2142 6ed0bbce Luca Bigliardi
    logging.error("Cannot set memory lock: %s",
2143 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
2144 4b6fa0bf Luca Bigliardi
    return
2145 4b6fa0bf Luca Bigliardi
2146 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
2147 4b6fa0bf Luca Bigliardi
2148 4b6fa0bf Luca Bigliardi
2149 0070a462 Renรฉ Nussbaumer
def Daemonize(logfile):
2150 7d88772a Iustin Pop
  """Daemonize the current process.
2151 7d88772a Iustin Pop

2152 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
2153 7d88772a Iustin Pop
  runs it in the background as a daemon.
2154 7d88772a Iustin Pop

2155 7d88772a Iustin Pop
  @type logfile: str
2156 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2157 7d88772a Iustin Pop
  @rtype: int
2158 5fcc718f Iustin Pop
  @return: the value zero
2159 7d88772a Iustin Pop

2160 7d88772a Iustin Pop
  """
2161 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
2162 7260cfbe Iustin Pop
  # yes, we really want os._exit
2163 8f765069 Iustin Pop
2164 b78aa8c2 Iustin Pop
  # TODO: do another attempt to merge Daemonize and StartDaemon, or at
2165 b78aa8c2 Iustin Pop
  # least abstract the pipe functionality between them
2166 b78aa8c2 Iustin Pop
2167 b78aa8c2 Iustin Pop
  # Create pipe for sending error messages
2168 b78aa8c2 Iustin Pop
  (rpipe, wpipe) = os.pipe()
2169 b78aa8c2 Iustin Pop
2170 8f765069 Iustin Pop
  # this might fail
2171 8f765069 Iustin Pop
  pid = os.fork()
2172 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
2173 0260032c Iustin Pop
    SetupDaemonEnv()
2174 0260032c Iustin Pop
2175 8f765069 Iustin Pop
    # this might fail
2176 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
2177 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
2178 b78aa8c2 Iustin Pop
      _CloseFDNoErr(rpipe)
2179 8f765069 Iustin Pop
    else:
2180 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
2181 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
2182 8f765069 Iustin Pop
  else:
2183 b78aa8c2 Iustin Pop
    _CloseFDNoErr(wpipe)
2184 b78aa8c2 Iustin Pop
    # Wait for daemon to be started (or an error message to
2185 b78aa8c2 Iustin Pop
    # arrive) and read up to 100 KB as an error message
2186 b78aa8c2 Iustin Pop
    errormsg = RetryOnSignal(os.read, rpipe, 100 * 1024)
2187 b78aa8c2 Iustin Pop
    if errormsg:
2188 b78aa8c2 Iustin Pop
      sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
2189 b78aa8c2 Iustin Pop
      rcode = 1
2190 b78aa8c2 Iustin Pop
    else:
2191 b78aa8c2 Iustin Pop
      rcode = 0
2192 b78aa8c2 Iustin Pop
    os._exit(rcode) # Exit parent of the first child.
2193 8f765069 Iustin Pop
2194 79634555 Iustin Pop
  SetupDaemonFDs(logfile, None)
2195 b78aa8c2 Iustin Pop
  return wpipe
2196 57c177af Iustin Pop
2197 57c177af Iustin Pop
2198 53beffbb Iustin Pop
def DaemonPidFileName(name):
2199 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
2200 58885d79 Iustin Pop

2201 58885d79 Iustin Pop
  @type name: str
2202 58885d79 Iustin Pop
  @param name: the daemon name
2203 58885d79 Iustin Pop
  @rtype: str
2204 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2205 58885d79 Iustin Pop
      daemon name
2206 b330ac0b Guido Trotter

2207 b330ac0b Guido Trotter
  """
2208 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2209 b330ac0b Guido Trotter
2210 b330ac0b Guido Trotter
2211 2826b361 Guido Trotter
def EnsureDaemon(name):
2212 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2213 2826b361 Guido Trotter

2214 2826b361 Guido Trotter
  """
2215 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2216 2826b361 Guido Trotter
  if result.failed:
2217 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2218 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2219 2826b361 Guido Trotter
    return False
2220 2826b361 Guido Trotter
2221 2826b361 Guido Trotter
  return True
2222 b330ac0b Guido Trotter
2223 b330ac0b Guido Trotter
2224 db147305 Tom Limoncelli
def StopDaemon(name):
2225 db147305 Tom Limoncelli
  """Stop daemon
2226 db147305 Tom Limoncelli

2227 db147305 Tom Limoncelli
  """
2228 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
2229 db147305 Tom Limoncelli
  if result.failed:
2230 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
2231 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
2232 db147305 Tom Limoncelli
    return False
2233 db147305 Tom Limoncelli
2234 db147305 Tom Limoncelli
  return True
2235 db147305 Tom Limoncelli
2236 db147305 Tom Limoncelli
2237 5c4d37f9 Iustin Pop
def WritePidFile(pidfile):
2238 b330ac0b Guido Trotter
  """Write the current process pidfile.
2239 b330ac0b Guido Trotter

2240 614244bd Iustin Pop
  @type pidfile: sting
2241 614244bd Iustin Pop
  @param pidfile: the path to the file to be written
2242 614244bd Iustin Pop
  @raise errors.LockError: if the pid file already exists and
2243 58885d79 Iustin Pop
      points to a live process
2244 614244bd Iustin Pop
  @rtype: int
2245 614244bd Iustin Pop
  @return: the file descriptor of the lock file; do not close this unless
2246 614244bd Iustin Pop
      you want to unlock the pid file
2247 b330ac0b Guido Trotter

2248 b330ac0b Guido Trotter
  """
2249 5c4d37f9 Iustin Pop
  # We don't rename nor truncate the file to not drop locks under
2250 5c4d37f9 Iustin Pop
  # existing processes
2251 5c4d37f9 Iustin Pop
  fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
2252 5c4d37f9 Iustin Pop
2253 5c4d37f9 Iustin Pop
  # Lock the PID file (and fail if not possible to do so). Any code
2254 5c4d37f9 Iustin Pop
  # wanting to send a signal to the daemon should try to lock the PID
2255 5c4d37f9 Iustin Pop
  # file before reading it. If acquiring the lock succeeds, the daemon is
2256 5c4d37f9 Iustin Pop
  # no longer running and the signal should not be sent.
2257 5c4d37f9 Iustin Pop
  LockFile(fd_pidfile)
2258 5c4d37f9 Iustin Pop
2259 5c4d37f9 Iustin Pop
  os.write(fd_pidfile, "%d\n" % os.getpid())
2260 b330ac0b Guido Trotter
2261 5c4d37f9 Iustin Pop
  return fd_pidfile
2262 b330ac0b Guido Trotter
2263 b330ac0b Guido Trotter
2264 b330ac0b Guido Trotter
def RemovePidFile(name):
2265 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2266 b330ac0b Guido Trotter

2267 b330ac0b Guido Trotter
  Any errors are ignored.
2268 b330ac0b Guido Trotter

2269 58885d79 Iustin Pop
  @type name: str
2270 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2271 58885d79 Iustin Pop

2272 b330ac0b Guido Trotter
  """
2273 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2274 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2275 b330ac0b Guido Trotter
  try:
2276 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2277 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2278 b330ac0b Guido Trotter
    pass
2279 b330ac0b Guido Trotter
2280 b330ac0b Guido Trotter
2281 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2282 ff5251bc Iustin Pop
                waitpid=False):
2283 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2284 b2a1f511 Iustin Pop

2285 b2a1f511 Iustin Pop
  @type pid: int
2286 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
2287 38206f3c Iustin Pop
  @type signal_: int
2288 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
2289 b2a1f511 Iustin Pop
  @type timeout: int
2290 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
2291 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
2292 b2a1f511 Iustin Pop
                  will be done
2293 ff5251bc Iustin Pop
  @type waitpid: boolean
2294 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
2295 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
2296 ff5251bc Iustin Pop
      would remain as zombie
2297 b2a1f511 Iustin Pop

2298 b2a1f511 Iustin Pop
  """
2299 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
2300 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
2301 560cbec1 Michael Hanselmann
    if IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
2302 ff5251bc Iustin Pop
      try:
2303 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
2304 ff5251bc Iustin Pop
      except OSError:
2305 ff5251bc Iustin Pop
        pass
2306 ff5251bc Iustin Pop
2307 b2a1f511 Iustin Pop
  if pid <= 0:
2308 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
2309 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
2310 b2a1f511 Iustin Pop
2311 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
2312 b2a1f511 Iustin Pop
    return
2313 31892b4c Michael Hanselmann
2314 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
2315 31892b4c Michael Hanselmann
2316 b2a1f511 Iustin Pop
  if timeout <= 0:
2317 b2a1f511 Iustin Pop
    return
2318 7167159a Michael Hanselmann
2319 31892b4c Michael Hanselmann
  def _CheckProcess():
2320 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
2321 31892b4c Michael Hanselmann
      return
2322 31892b4c Michael Hanselmann
2323 7167159a Michael Hanselmann
    try:
2324 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
2325 7167159a Michael Hanselmann
    except OSError:
2326 31892b4c Michael Hanselmann
      raise RetryAgain()
2327 31892b4c Michael Hanselmann
2328 31892b4c Michael Hanselmann
    if result_pid > 0:
2329 31892b4c Michael Hanselmann
      return
2330 31892b4c Michael Hanselmann
2331 31892b4c Michael Hanselmann
    raise RetryAgain()
2332 31892b4c Michael Hanselmann
2333 31892b4c Michael Hanselmann
  try:
2334 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
2335 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
2336 31892b4c Michael Hanselmann
  except RetryTimeout:
2337 31892b4c Michael Hanselmann
    pass
2338 7167159a Michael Hanselmann
2339 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
2340 7167159a Michael Hanselmann
    # Kill process if it's still alive
2341 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
2342 b2a1f511 Iustin Pop
2343 b2a1f511 Iustin Pop
2344 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
2345 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
2346 57c177af Iustin Pop

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

2350 58885d79 Iustin Pop
  @type name: str
2351 58885d79 Iustin Pop
  @param name: the name to look for
2352 58885d79 Iustin Pop
  @type search_path: str
2353 58885d79 Iustin Pop
  @param search_path: location to start at
2354 58885d79 Iustin Pop
  @type test: callable
2355 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2356 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2357 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2358 58885d79 Iustin Pop
  @rtype: str or None
2359 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2360 57c177af Iustin Pop

2361 57c177af Iustin Pop
  """
2362 f95c81bf Iustin Pop
  # validate the filename mask
2363 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
2364 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
2365 f95c81bf Iustin Pop
                     name)
2366 f95c81bf Iustin Pop
    return None
2367 f95c81bf Iustin Pop
2368 57c177af Iustin Pop
  for dir_name in search_path:
2369 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
2370 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
2371 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
2372 f95c81bf Iustin Pop
    # basename
2373 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
2374 57c177af Iustin Pop
      return item_name
2375 57c177af Iustin Pop
  return None
2376 8d1a2a64 Michael Hanselmann
2377 8d1a2a64 Michael Hanselmann
2378 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
2379 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
2380 8d1a2a64 Michael Hanselmann

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

2384 58885d79 Iustin Pop
  @type vglist: dict
2385 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2386 58885d79 Iustin Pop
  @type vgname: str
2387 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2388 58885d79 Iustin Pop
  @type minsize: int
2389 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2390 58885d79 Iustin Pop
  @rtype: None or str
2391 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2392 8d1a2a64 Michael Hanselmann

2393 8d1a2a64 Michael Hanselmann
  """
2394 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2395 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2396 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2397 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2398 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2399 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2400 8d1a2a64 Michael Hanselmann
  return None
2401 7996a135 Iustin Pop
2402 7996a135 Iustin Pop
2403 45bc5e4a Michael Hanselmann
def SplitTime(value):
2404 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2405 739be818 Michael Hanselmann

2406 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2407 45bc5e4a Michael Hanselmann
  @type value: int or float
2408 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2409 739be818 Michael Hanselmann

2410 739be818 Michael Hanselmann
  """
2411 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2412 45bc5e4a Michael Hanselmann
2413 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2414 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2415 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2416 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2417 45bc5e4a Michael Hanselmann
2418 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2419 739be818 Michael Hanselmann
2420 739be818 Michael Hanselmann
2421 739be818 Michael Hanselmann
def MergeTime(timetuple):
2422 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2423 739be818 Michael Hanselmann

2424 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2425 739be818 Michael Hanselmann
  @type timetuple: tuple
2426 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2427 739be818 Michael Hanselmann

2428 739be818 Michael Hanselmann
  """
2429 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2430 739be818 Michael Hanselmann
2431 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2432 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2433 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2434 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2435 739be818 Michael Hanselmann
2436 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2437 739be818 Michael Hanselmann
2438 739be818 Michael Hanselmann
2439 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2440 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2441 de3b8e39 Luca Bigliardi

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

2446 de3b8e39 Luca Bigliardi
  """
2447 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2448 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2449 de3b8e39 Luca Bigliardi

2450 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2451 de3b8e39 Luca Bigliardi

2452 de3b8e39 Luca Bigliardi
    """
2453 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2454 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2455 de3b8e39 Luca Bigliardi
2456 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2457 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2458 de3b8e39 Luca Bigliardi

2459 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2460 de3b8e39 Luca Bigliardi
    /dev/console.
2461 de3b8e39 Luca Bigliardi

2462 de3b8e39 Luca Bigliardi
    """
2463 de3b8e39 Luca Bigliardi
    try:
2464 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2465 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2466 de3b8e39 Luca Bigliardi
      try:
2467 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2468 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2469 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2470 de3b8e39 Luca Bigliardi
        pass
2471 de3b8e39 Luca Bigliardi
2472 de3b8e39 Luca Bigliardi
2473 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2474 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2475 49e60a28 Luca Bigliardi
                 console_logging=False):
2476 82d9caef Iustin Pop
  """Configures the logging module.
2477 82d9caef Iustin Pop

2478 58885d79 Iustin Pop
  @type logfile: str
2479 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2480 ea34193f Iustin Pop
  @type debug: integer
2481 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2482 58885d79 Iustin Pop
      only those at C{INFO} and above level
2483 58885d79 Iustin Pop
  @type stderr_logging: boolean
2484 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2485 58885d79 Iustin Pop
  @type program: str
2486 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2487 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2488 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2489 551b6283 Iustin Pop
  @type syslog: string
2490 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2491 551b6283 Iustin Pop
      - if no, syslog is not used
2492 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2493 551b6283 Iustin Pop
      - if only, only syslog is used
2494 49e60a28 Luca Bigliardi
  @type console_logging: boolean
2495 49e60a28 Luca Bigliardi
  @param console_logging: if True, will use a FileHandler which falls back to
2496 49e60a28 Luca Bigliardi
      the system console if logging fails
2497 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2498 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2499 58885d79 Iustin Pop

2500 82d9caef Iustin Pop
  """
2501 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2502 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2503 d21d09d6 Iustin Pop
  if multithreaded:
2504 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2505 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2506 82d9caef Iustin Pop
  if debug:
2507 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2508 551b6283 Iustin Pop
    # no debug info for syslog loggers
2509 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2510 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2511 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2512 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2513 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2514 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2515 82d9caef Iustin Pop
2516 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2517 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2518 82d9caef Iustin Pop
2519 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2520 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2521 7d88772a Iustin Pop
    handler.close()
2522 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2523 6346a9e5 Michael Hanselmann
2524 82d9caef Iustin Pop
  if stderr_logging:
2525 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2526 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2527 82d9caef Iustin Pop
    if debug:
2528 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2529 82d9caef Iustin Pop
    else:
2530 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2531 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2532 82d9caef Iustin Pop
2533 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2534 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2535 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2536 551b6283 Iustin Pop
                                                    facility)
2537 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2538 551b6283 Iustin Pop
    # Never enable debug over syslog
2539 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2540 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2541 551b6283 Iustin Pop
2542 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2543 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2544 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2545 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2546 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2547 551b6283 Iustin Pop
    try:
2548 49e60a28 Luca Bigliardi
      if console_logging:
2549 49e60a28 Luca Bigliardi
        logfile_handler = LogFileHandler(logfile)
2550 49e60a28 Luca Bigliardi
      else:
2551 49e60a28 Luca Bigliardi
        logfile_handler = logging.FileHandler(logfile)
2552 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2553 551b6283 Iustin Pop
      if debug:
2554 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2555 551b6283 Iustin Pop
      else:
2556 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2557 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2558 551b6283 Iustin Pop
    except EnvironmentError:
2559 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2560 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2561 551b6283 Iustin Pop
      else:
2562 551b6283 Iustin Pop
        # we need to re-raise the exception
2563 551b6283 Iustin Pop
        raise
2564 82d9caef Iustin Pop
2565 016d04b3 Michael Hanselmann
2566 da961187 Guido Trotter
def IsNormAbsPath(path):
2567 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2568 da961187 Guido Trotter

2569 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2570 da961187 Guido Trotter

2571 da961187 Guido Trotter
  """
2572 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2573 82d9caef Iustin Pop
2574 016d04b3 Michael Hanselmann
2575 4bb678e9 Iustin Pop
def PathJoin(*args):
2576 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2577 4bb678e9 Iustin Pop

2578 4bb678e9 Iustin Pop
  Requirements:
2579 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2580 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2581 4bb678e9 Iustin Pop
        since we check for normalization at the end
2582 4bb678e9 Iustin Pop

2583 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2584 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2585 4bb678e9 Iustin Pop

2586 4bb678e9 Iustin Pop
  """
2587 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2588 4bb678e9 Iustin Pop
  assert args
2589 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2590 4bb678e9 Iustin Pop
  root = args[0]
2591 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2592 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2593 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2594 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2595 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2596 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2597 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2598 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2599 4bb678e9 Iustin Pop
  if prefix != root:
2600 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2601 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2602 4bb678e9 Iustin Pop
  return result
2603 4bb678e9 Iustin Pop
2604 4bb678e9 Iustin Pop
2605 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2606 f65f63ef Iustin Pop
  """Return the last lines from a file.
2607 f65f63ef Iustin Pop

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

2612 f65f63ef Iustin Pop
  @param fname: the file name
2613 f65f63ef Iustin Pop
  @type lines: int
2614 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2615 f65f63ef Iustin Pop

2616 f65f63ef Iustin Pop
  """
2617 f65f63ef Iustin Pop
  fd = open(fname, "r")
2618 f65f63ef Iustin Pop
  try:
2619 f65f63ef Iustin Pop
    fd.seek(0, 2)
2620 f65f63ef Iustin Pop
    pos = fd.tell()
2621 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2622 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2623 f65f63ef Iustin Pop
    raw_data = fd.read()
2624 f65f63ef Iustin Pop
  finally:
2625 f65f63ef Iustin Pop
    fd.close()
2626 f65f63ef Iustin Pop
2627 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2628 f65f63ef Iustin Pop
  return rows[-lines:]
2629 f65f63ef Iustin Pop
2630 f65f63ef Iustin Pop
2631 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2632 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2633 24d70417 Michael Hanselmann

2634 24d70417 Michael Hanselmann
  """
2635 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2636 24d70417 Michael Hanselmann
2637 24d70417 Michael Hanselmann
2638 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2639 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2640 27e46076 Michael Hanselmann

2641 27e46076 Michael Hanselmann
  @type value: string
2642 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2643 27e46076 Michael Hanselmann

2644 27e46076 Michael Hanselmann
  """
2645 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2646 27e46076 Michael Hanselmann
  if m:
2647 27e46076 Michael Hanselmann
    # We have an offset
2648 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2649 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2650 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2651 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2652 27e46076 Michael Hanselmann
  else:
2653 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2654 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2655 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2656 27e46076 Michael Hanselmann
    utcoffset = 0
2657 27e46076 Michael Hanselmann
2658 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2659 27e46076 Michael Hanselmann
2660 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2661 27e46076 Michael Hanselmann
2662 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2663 27e46076 Michael Hanselmann
2664 27e46076 Michael Hanselmann
2665 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2666 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2667 27e46076 Michael Hanselmann

2668 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2669 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2670 27e46076 Michael Hanselmann

2671 27e46076 Michael Hanselmann
  """
2672 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2673 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2674 27e46076 Michael Hanselmann
  try:
2675 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2676 27e46076 Michael Hanselmann
  except AttributeError:
2677 27e46076 Michael Hanselmann
    not_before = None
2678 27e46076 Michael Hanselmann
  else:
2679 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2680 27e46076 Michael Hanselmann
2681 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2682 27e46076 Michael Hanselmann
      not_before = None
2683 27e46076 Michael Hanselmann
    else:
2684 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2685 27e46076 Michael Hanselmann
2686 27e46076 Michael Hanselmann
  try:
2687 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2688 27e46076 Michael Hanselmann
  except AttributeError:
2689 27e46076 Michael Hanselmann
    not_after = None
2690 27e46076 Michael Hanselmann
  else:
2691 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2692 27e46076 Michael Hanselmann
2693 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2694 27e46076 Michael Hanselmann
      not_after = None
2695 27e46076 Michael Hanselmann
    else:
2696 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2697 27e46076 Michael Hanselmann
2698 27e46076 Michael Hanselmann
  return (not_before, not_after)
2699 27e46076 Michael Hanselmann
2700 27e46076 Michael Hanselmann
2701 24d70417 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
2702 24d70417 Michael Hanselmann
                            warn_days, error_days):
2703 24d70417 Michael Hanselmann
  """Verifies certificate validity.
2704 24d70417 Michael Hanselmann

2705 24d70417 Michael Hanselmann
  @type expired: bool
2706 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2707 24d70417 Michael Hanselmann
  @type not_before: number or None
2708 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2709 24d70417 Michael Hanselmann
  @type not_after: number or None
2710 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2711 24d70417 Michael Hanselmann
  @type now: number
2712 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2713 24d70417 Michael Hanselmann
  @type warn_days: number or None
2714 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2715 24d70417 Michael Hanselmann
  @type error_days: number or None
2716 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2717 24d70417 Michael Hanselmann

2718 24d70417 Michael Hanselmann
  """
2719 24d70417 Michael Hanselmann
  if expired:
2720 24d70417 Michael Hanselmann
    msg = "Certificate is expired"
2721 24d70417 Michael Hanselmann
2722 24d70417 Michael Hanselmann
    if not_before is not None and not_after is not None:
2723 24d70417 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
2724 24d70417 Michael Hanselmann
              (FormatTimestampWithTZ(not_before),
2725 24d70417 Michael Hanselmann
               FormatTimestampWithTZ(not_after)))
2726 24d70417 Michael Hanselmann
    elif not_before is not None:
2727 24d70417 Michael Hanselmann
      msg += " (valid from %s)" % FormatTimestampWithTZ(not_before)
2728 24d70417 Michael Hanselmann
    elif not_after is not None:
2729 24d70417 Michael Hanselmann
      msg += " (valid until %s)" % FormatTimestampWithTZ(not_after)
2730 24d70417 Michael Hanselmann
2731 24d70417 Michael Hanselmann
    return (CERT_ERROR, msg)
2732 24d70417 Michael Hanselmann
2733 24d70417 Michael Hanselmann
  elif not_before is not None and not_before > now:
2734 24d70417 Michael Hanselmann
    return (CERT_WARNING,
2735 24d70417 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
2736 24d70417 Michael Hanselmann
            FormatTimestampWithTZ(not_before))
2737 24d70417 Michael Hanselmann
2738 24d70417 Michael Hanselmann
  elif not_after is not None:
2739 24d70417 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
2740 24d70417 Michael Hanselmann
2741 24d70417 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
2742 24d70417 Michael Hanselmann
2743 24d70417 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
2744 24d70417 Michael Hanselmann
      return (CERT_ERROR, msg)
2745 24d70417 Michael Hanselmann
2746 24d70417 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
2747 24d70417 Michael Hanselmann
      return (CERT_WARNING, msg)
2748 24d70417 Michael Hanselmann
2749 24d70417 Michael Hanselmann
  return (None, None)
2750 24d70417 Michael Hanselmann
2751 24d70417 Michael Hanselmann
2752 24d70417 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
2753 24d70417 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
2754 24d70417 Michael Hanselmann

2755 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2756 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2757 24d70417 Michael Hanselmann
  @type warn_days: number or None
2758 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2759 24d70417 Michael Hanselmann
  @type error_days: number or None
2760 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2761 24d70417 Michael Hanselmann

2762 24d70417 Michael Hanselmann
  """
2763 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2764 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2765 24d70417 Michael Hanselmann
2766 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2767 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2768 24d70417 Michael Hanselmann
2769 24d70417 Michael Hanselmann
2770 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2771 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2772 68857643 Michael Hanselmann

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

2775 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2776 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2777 68857643 Michael Hanselmann
  @type key: string
2778 68857643 Michael Hanselmann
  @param key: Key for HMAC
2779 68857643 Michael Hanselmann
  @type salt: string
2780 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2781 68857643 Michael Hanselmann
  @rtype: string
2782 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2783 68857643 Michael Hanselmann

2784 68857643 Michael Hanselmann
  """
2785 68857643 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2786 68857643 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
2787 68857643 Michael Hanselmann
2788 68857643 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
2789 68857643 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2790 68857643 Michael Hanselmann
2791 68857643 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
2792 68857643 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
2793 3718bf6d Michael Hanselmann
           Sha1Hmac(key, cert_pem, salt=salt),
2794 68857643 Michael Hanselmann
           cert_pem))
2795 68857643 Michael Hanselmann
2796 68857643 Michael Hanselmann
2797 68857643 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
2798 68857643 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
2799 68857643 Michael Hanselmann

2800 68857643 Michael Hanselmann
  """
2801 68857643 Michael Hanselmann
  # Extract signature from original PEM data
2802 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
2803 68857643 Michael Hanselmann
    if line.startswith("---"):
2804 68857643 Michael Hanselmann
      break
2805 68857643 Michael Hanselmann
2806 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
2807 68857643 Michael Hanselmann
    if m:
2808 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
2809 68857643 Michael Hanselmann
2810 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
2811 68857643 Michael Hanselmann
2812 68857643 Michael Hanselmann
2813 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
2814 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
2815 68857643 Michael Hanselmann

2816 68857643 Michael Hanselmann
  @type cert_pem: string
2817 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
2818 68857643 Michael Hanselmann
  @type key: string
2819 68857643 Michael Hanselmann
  @param key: Key for HMAC
2820 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
2821 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
2822 68857643 Michael Hanselmann

2823 68857643 Michael Hanselmann
  """
2824 68857643 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
2825 68857643 Michael Hanselmann
2826 68857643 Michael Hanselmann
  # Load certificate
2827 68857643 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
2828 68857643 Michael Hanselmann
2829 68857643 Michael Hanselmann
  # Dump again to ensure it's in a sane format
2830 68857643 Michael Hanselmann
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2831 68857643 Michael Hanselmann
2832 3718bf6d Michael Hanselmann
  if not VerifySha1Hmac(key, sane_pem, signature, salt=salt):
2833 68857643 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
2834 68857643 Michael Hanselmann
2835 68857643 Michael Hanselmann
  return (cert, salt)
2836 68857643 Michael Hanselmann
2837 68857643 Michael Hanselmann
2838 3718bf6d Michael Hanselmann
def Sha1Hmac(key, text, salt=None):
2839 615aaaba Michael Hanselmann
  """Calculates the HMAC-SHA1 digest of a text.
2840 615aaaba Michael Hanselmann

2841 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2842 615aaaba Michael Hanselmann

2843 615aaaba Michael Hanselmann
  @type key: string
2844 615aaaba Michael Hanselmann
  @param key: Secret key
2845 615aaaba Michael Hanselmann
  @type text: string
2846 615aaaba Michael Hanselmann

2847 615aaaba Michael Hanselmann
  """
2848 3718bf6d Michael Hanselmann
  if salt:
2849 3718bf6d Michael Hanselmann
    salted_text = salt + text
2850 3718bf6d Michael Hanselmann
  else:
2851 3718bf6d Michael Hanselmann
    salted_text = text
2852 3718bf6d Michael Hanselmann
2853 716a32cb Guido Trotter
  return hmac.new(key, salted_text, compat.sha1).hexdigest()
2854 615aaaba Michael Hanselmann
2855 615aaaba Michael Hanselmann
2856 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
2857 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
2858 615aaaba Michael Hanselmann

2859 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2860 615aaaba Michael Hanselmann

2861 615aaaba Michael Hanselmann
  @type key: string
2862 615aaaba Michael Hanselmann
  @param key: Secret key
2863 615aaaba Michael Hanselmann
  @type text: string
2864 615aaaba Michael Hanselmann
  @type digest: string
2865 615aaaba Michael Hanselmann
  @param digest: Expected digest
2866 615aaaba Michael Hanselmann
  @rtype: bool
2867 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
2868 615aaaba Michael Hanselmann

2869 615aaaba Michael Hanselmann
  """
2870 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
2871 615aaaba Michael Hanselmann
2872 615aaaba Michael Hanselmann
2873 26f15862 Iustin Pop
def SafeEncode(text):
2874 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2875 26f15862 Iustin Pop

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

2885 26f15862 Iustin Pop
  @type text: str or unicode
2886 26f15862 Iustin Pop
  @param text: input data
2887 26f15862 Iustin Pop
  @rtype: str
2888 26f15862 Iustin Pop
  @return: a safe version of text
2889 26f15862 Iustin Pop

2890 26f15862 Iustin Pop
  """
2891 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2892 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2893 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2894 d392fa34 Iustin Pop
  resu = ""
2895 d392fa34 Iustin Pop
  for char in text:
2896 d392fa34 Iustin Pop
    c = ord(char)
2897 d392fa34 Iustin Pop
    if char  == '\t':
2898 d392fa34 Iustin Pop
      resu += r'\t'
2899 d392fa34 Iustin Pop
    elif char == '\n':
2900 d392fa34 Iustin Pop
      resu += r'\n'
2901 d392fa34 Iustin Pop
    elif char == '\r':
2902 d392fa34 Iustin Pop
      resu += r'\'r'
2903 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2904 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2905 d392fa34 Iustin Pop
    else:
2906 d392fa34 Iustin Pop
      resu += char
2907 d392fa34 Iustin Pop
  return resu
2908 26f15862 Iustin Pop
2909 26f15862 Iustin Pop
2910 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2911 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2912 5b69bc7c Iustin Pop

2913 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2914 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2915 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2916 5b69bc7c Iustin Pop
  separator):
2917 5b69bc7c Iustin Pop
    - a plain , separates the elements
2918 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2919 5b69bc7c Iustin Pop
      backslash plus a separator comma
2920 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2921 5b69bc7c Iustin Pop
      non-separator comma
2922 5b69bc7c Iustin Pop

2923 5b69bc7c Iustin Pop
  @type text: string
2924 5b69bc7c Iustin Pop
  @param text: the string to split
2925 5b69bc7c Iustin Pop
  @type sep: string
2926 5b69bc7c Iustin Pop
  @param text: the separator
2927 5b69bc7c Iustin Pop
  @rtype: string
2928 5b69bc7c Iustin Pop
  @return: a list of strings
2929 5b69bc7c Iustin Pop

2930 5b69bc7c Iustin Pop
  """
2931 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2932 5b69bc7c Iustin Pop
  slist = text.split(sep)
2933 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2934 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2935 5b69bc7c Iustin Pop
  rlist = []
2936 5b69bc7c Iustin Pop
  while slist:
2937 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2938 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2939 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2940 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2941 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2942 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2943 5b69bc7c Iustin Pop
        # the next step
2944 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2945 5b69bc7c Iustin Pop
        continue
2946 5b69bc7c Iustin Pop
    rlist.append(e1)
2947 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2948 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2949 5b69bc7c Iustin Pop
  return rlist
2950 5b69bc7c Iustin Pop
2951 5b69bc7c Iustin Pop
2952 ab3e6da8 Iustin Pop
def CommaJoin(names):
2953 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2954 ab3e6da8 Iustin Pop

2955 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2956 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2957 ab3e6da8 Iustin Pop

2958 ab3e6da8 Iustin Pop
  """
2959 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2960 ab3e6da8 Iustin Pop
2961 ab3e6da8 Iustin Pop
2962 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2963 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2964 3f6a47a8 Michael Hanselmann

2965 3f6a47a8 Michael Hanselmann
  @type value: int
2966 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2967 3f6a47a8 Michael Hanselmann
  @rtype: int
2968 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2969 3f6a47a8 Michael Hanselmann

2970 3f6a47a8 Michael Hanselmann
  """
2971 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2972 3f6a47a8 Michael Hanselmann
2973 3f6a47a8 Michael Hanselmann
2974 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2975 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2976 3f6a47a8 Michael Hanselmann

2977 3f6a47a8 Michael Hanselmann
  @type path: string
2978 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2979 3f6a47a8 Michael Hanselmann
  @rtype: int
2980 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2981 3f6a47a8 Michael Hanselmann

2982 3f6a47a8 Michael Hanselmann
  """
2983 3f6a47a8 Michael Hanselmann
  size = 0
2984 3f6a47a8 Michael Hanselmann
2985 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2986 2a887df9 Michael Hanselmann
    for filename in files:
2987 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2988 3f6a47a8 Michael Hanselmann
      size += st.st_size
2989 3f6a47a8 Michael Hanselmann
2990 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2991 3f6a47a8 Michael Hanselmann
2992 3f6a47a8 Michael Hanselmann
2993 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
2994 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
2995 1b045f5d Balazs Lecz

2996 1b045f5d Balazs Lecz
  This function is Linux-specific.
2997 1b045f5d Balazs Lecz

2998 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
2999 1b045f5d Balazs Lecz
  @rtype: list of tuples
3000 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
3001 1b045f5d Balazs Lecz

3002 1b045f5d Balazs Lecz
  """
3003 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
3004 1b045f5d Balazs Lecz
  data = []
3005 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
3006 1b045f5d Balazs Lecz
  for line in mountlines:
3007 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
3008 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
3009 1b045f5d Balazs Lecz
3010 1b045f5d Balazs Lecz
  return data
3011 1b045f5d Balazs Lecz
3012 1b045f5d Balazs Lecz
3013 620a85fd Iustin Pop
def GetFilesystemStats(path):
3014 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
3015 3f6a47a8 Michael Hanselmann

3016 3f6a47a8 Michael Hanselmann
  @type path: string
3017 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
3018 3f6a47a8 Michael Hanselmann
  @rtype: int
3019 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
3020 3f6a47a8 Michael Hanselmann

3021 3f6a47a8 Michael Hanselmann
  """
3022 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
3023 3f6a47a8 Michael Hanselmann
3024 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
3025 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
3026 620a85fd Iustin Pop
  return (tsize, fsize)
3027 3f6a47a8 Michael Hanselmann
3028 3f6a47a8 Michael Hanselmann
3029 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
3030 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
3031 eb58f7bd Michael Hanselmann

3032 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
3033 eb58f7bd Michael Hanselmann

3034 eb58f7bd Michael Hanselmann
  @type fn: callable
3035 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
3036 bdefe5dd Michael Hanselmann
  @rtype: bool
3037 bdefe5dd Michael Hanselmann
  @return: Function's result
3038 eb58f7bd Michael Hanselmann

3039 eb58f7bd Michael Hanselmann
  """
3040 eb58f7bd Michael Hanselmann
  pid = os.fork()
3041 eb58f7bd Michael Hanselmann
  if pid == 0:
3042 eb58f7bd Michael Hanselmann
    # Child process
3043 eb58f7bd Michael Hanselmann
    try:
3044 82869978 Michael Hanselmann
      # In case the function uses temporary files
3045 82869978 Michael Hanselmann
      ResetTempfileModule()
3046 82869978 Michael Hanselmann
3047 eb58f7bd Michael Hanselmann
      # Call function
3048 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
3049 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
3050 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
3051 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
3052 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
3053 eb58f7bd Michael Hanselmann
      result = 33
3054 eb58f7bd Michael Hanselmann
3055 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
3056 eb58f7bd Michael Hanselmann
3057 eb58f7bd Michael Hanselmann
  # Parent process
3058 eb58f7bd Michael Hanselmann
3059 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
3060 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
3061 eb58f7bd Michael Hanselmann
3062 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
3063 eb58f7bd Michael Hanselmann
    exitcode = None
3064 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
3065 eb58f7bd Michael Hanselmann
  else:
3066 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
3067 eb58f7bd Michael Hanselmann
    signum = None
3068 eb58f7bd Michael Hanselmann
3069 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
3070 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
3071 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
3072 eb58f7bd Michael Hanselmann
3073 eb58f7bd Michael Hanselmann
  return bool(exitcode)
3074 eb58f7bd Michael Hanselmann
3075 eb58f7bd Michael Hanselmann
3076 560cbec1 Michael Hanselmann
def IgnoreProcessNotFound(fn, *args, **kwargs):
3077 560cbec1 Michael Hanselmann
  """Ignores ESRCH when calling a process-related function.
3078 560cbec1 Michael Hanselmann

3079 560cbec1 Michael Hanselmann
  ESRCH is raised when a process is not found.
3080 560cbec1 Michael Hanselmann

3081 560cbec1 Michael Hanselmann
  @rtype: bool
3082 560cbec1 Michael Hanselmann
  @return: Whether process was found
3083 560cbec1 Michael Hanselmann

3084 560cbec1 Michael Hanselmann
  """
3085 560cbec1 Michael Hanselmann
  try:
3086 560cbec1 Michael Hanselmann
    fn(*args, **kwargs)
3087 560cbec1 Michael Hanselmann
  except EnvironmentError, err:
3088 560cbec1 Michael Hanselmann
    # Ignore ESRCH
3089 560cbec1 Michael Hanselmann
    if err.errno == errno.ESRCH:
3090 560cbec1 Michael Hanselmann
      return False
3091 560cbec1 Michael Hanselmann
    raise
3092 560cbec1 Michael Hanselmann
3093 560cbec1 Michael Hanselmann
  return True
3094 560cbec1 Michael Hanselmann
3095 560cbec1 Michael Hanselmann
3096 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
3097 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
3098 232144d0 Guido Trotter

3099 232144d0 Guido Trotter
  """
3100 232144d0 Guido Trotter
  try:
3101 232144d0 Guido Trotter
    return fn(*args, **kwargs)
3102 965d0e5b Guido Trotter
  except EnvironmentError, err:
3103 2fd7f564 Guido Trotter
    if err.errno == errno.EINTR:
3104 2fd7f564 Guido Trotter
      return None
3105 2fd7f564 Guido Trotter
    else:
3106 232144d0 Guido Trotter
      raise
3107 965d0e5b Guido Trotter
  except (select.error, socket.error), err:
3108 965d0e5b Guido Trotter
    # In python 2.6 and above select.error is an IOError, so it's handled
3109 965d0e5b Guido Trotter
    # above, in 2.5 and below it's not, and it's handled here.
3110 2fd7f564 Guido Trotter
    if err.args and err.args[0] == errno.EINTR:
3111 2fd7f564 Guido Trotter
      return None
3112 2fd7f564 Guido Trotter
    else:
3113 232144d0 Guido Trotter
      raise
3114 232144d0 Guido Trotter
3115 232144d0 Guido Trotter
3116 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
3117 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
3118 eb0f0ce0 Michael Hanselmann

3119 58885d79 Iustin Pop
  @type fd: int
3120 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
3121 58885d79 Iustin Pop

3122 eb0f0ce0 Michael Hanselmann
  """
3123 eb0f0ce0 Michael Hanselmann
  try:
3124 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
3125 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3126 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3127 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3128 eb0f0ce0 Michael Hanselmann
    raise
3129 de499029 Michael Hanselmann
3130 de499029 Michael Hanselmann
3131 3b813dd2 Iustin Pop
def FormatTime(val):
3132 3b813dd2 Iustin Pop
  """Formats a time value.
3133 3b813dd2 Iustin Pop

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

3138 3b813dd2 Iustin Pop
  """
3139 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3140 3b813dd2 Iustin Pop
    return "N/A"
3141 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3142 3b813dd2 Iustin Pop
  # platforms
3143 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3144 3b813dd2 Iustin Pop
3145 3b813dd2 Iustin Pop
3146 f8ea4ada Michael Hanselmann
def FormatSeconds(secs):
3147 f8ea4ada Michael Hanselmann
  """Formats seconds for easier reading.
3148 f8ea4ada Michael Hanselmann

3149 f8ea4ada Michael Hanselmann
  @type secs: number
3150 f8ea4ada Michael Hanselmann
  @param secs: Number of seconds
3151 f8ea4ada Michael Hanselmann
  @rtype: string
3152 f8ea4ada Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
3153 f8ea4ada Michael Hanselmann

3154 f8ea4ada Michael Hanselmann
  """
3155 f8ea4ada Michael Hanselmann
  parts = []
3156 f8ea4ada Michael Hanselmann
3157 f8ea4ada Michael Hanselmann
  secs = round(secs, 0)
3158 f8ea4ada Michael Hanselmann
3159 f8ea4ada Michael Hanselmann
  if secs > 0:
3160 f8ea4ada Michael Hanselmann
    # Negative values would be a bit tricky
3161 f8ea4ada Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
3162 f8ea4ada Michael Hanselmann
      (complete, secs) = divmod(secs, one)
3163 f8ea4ada Michael Hanselmann
      if complete or parts:
3164 f8ea4ada Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
3165 f8ea4ada Michael Hanselmann
3166 f8ea4ada Michael Hanselmann
  parts.append("%ds" % secs)
3167 f8ea4ada Michael Hanselmann
3168 f8ea4ada Michael Hanselmann
  return " ".join(parts)
3169 f8ea4ada Michael Hanselmann
3170 f8ea4ada Michael Hanselmann
3171 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
3172 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
3173 05e50653 Michael Hanselmann

3174 5cbe43a5 Michael Hanselmann
  @type filename: string
3175 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3176 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3177 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3178 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3179 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3180 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3181 5cbe43a5 Michael Hanselmann

3182 05e50653 Michael Hanselmann
  """
3183 05e50653 Michael Hanselmann
  if now is None:
3184 05e50653 Michael Hanselmann
    now = time.time()
3185 05e50653 Michael Hanselmann
3186 05e50653 Michael Hanselmann
  try:
3187 05e50653 Michael Hanselmann
    value = ReadFile(filename)
3188 05e50653 Michael Hanselmann
  except IOError, err:
3189 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
3190 05e50653 Michael Hanselmann
      raise
3191 05e50653 Michael Hanselmann
    value = None
3192 05e50653 Michael Hanselmann
3193 05e50653 Michael Hanselmann
  if value is not None:
3194 05e50653 Michael Hanselmann
    try:
3195 05e50653 Michael Hanselmann
      value = int(value)
3196 05e50653 Michael Hanselmann
    except ValueError:
3197 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
3198 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
3199 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
3200 05e50653 Michael Hanselmann
      value = None
3201 05e50653 Michael Hanselmann
3202 05e50653 Michael Hanselmann
    if value is not None:
3203 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
3204 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
3205 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
3206 5cbe43a5 Michael Hanselmann
        value = None
3207 5cbe43a5 Michael Hanselmann
3208 5cbe43a5 Michael Hanselmann
      elif now > value:
3209 05e50653 Michael Hanselmann
        value = None
3210 05e50653 Michael Hanselmann
3211 05e50653 Michael Hanselmann
  return value
3212 05e50653 Michael Hanselmann
3213 05e50653 Michael Hanselmann
3214 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
3215 de0ea66b Michael Hanselmann
  """Retry loop timed out.
3216 de0ea66b Michael Hanselmann

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

3221 de0ea66b Michael Hanselmann
  """
3222 506be7c5 Guido Trotter
  def RaiseInner(self):
3223 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3224 506be7c5 Guido Trotter
      raise self.args[0]
3225 506be7c5 Guido Trotter
    else:
3226 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3227 de0ea66b Michael Hanselmann
3228 de0ea66b Michael Hanselmann
3229 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3230 de0ea66b Michael Hanselmann
  """Retry again.
3231 de0ea66b Michael Hanselmann

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

3236 de0ea66b Michael Hanselmann
  """
3237 de0ea66b Michael Hanselmann
3238 de0ea66b Michael Hanselmann
3239 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3240 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3241 de0ea66b Michael Hanselmann

3242 de0ea66b Michael Hanselmann
  """
3243 de0ea66b Michael Hanselmann
  __slots__ = [
3244 de0ea66b Michael Hanselmann
    "_factor",
3245 de0ea66b Michael Hanselmann
    "_limit",
3246 de0ea66b Michael Hanselmann
    "_next",
3247 de0ea66b Michael Hanselmann
    "_start",
3248 de0ea66b Michael Hanselmann
    ]
3249 de0ea66b Michael Hanselmann
3250 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3251 de0ea66b Michael Hanselmann
    """Initializes this class.
3252 de0ea66b Michael Hanselmann

3253 de0ea66b Michael Hanselmann
    @type start: float
3254 de0ea66b Michael Hanselmann
    @param start: Initial delay
3255 de0ea66b Michael Hanselmann
    @type factor: float
3256 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3257 de0ea66b Michael Hanselmann
    @type limit: float or None
3258 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3259 de0ea66b Michael Hanselmann

3260 de0ea66b Michael Hanselmann
    """
3261 de0ea66b Michael Hanselmann
    assert start > 0.0
3262 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3263 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3264 de0ea66b Michael Hanselmann
3265 de0ea66b Michael Hanselmann
    self._start = start
3266 de0ea66b Michael Hanselmann
    self._factor = factor
3267 de0ea66b Michael Hanselmann
    self._limit = limit
3268 de0ea66b Michael Hanselmann
3269 de0ea66b Michael Hanselmann
    self._next = start
3270 de0ea66b Michael Hanselmann
3271 de0ea66b Michael Hanselmann
  def __call__(self):
3272 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3273 de0ea66b Michael Hanselmann

3274 de0ea66b Michael Hanselmann
    """
3275 de0ea66b Michael Hanselmann
    current = self._next
3276 de0ea66b Michael Hanselmann
3277 de0ea66b Michael Hanselmann
    # Update for next run
3278 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3279 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3280 de0ea66b Michael Hanselmann
3281 de0ea66b Michael Hanselmann
    return current
3282 de0ea66b Michael Hanselmann
3283 de0ea66b Michael Hanselmann
3284 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3285 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3286 de0ea66b Michael Hanselmann
3287 de0ea66b Michael Hanselmann
3288 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3289 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3290 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3291 de0ea66b Michael Hanselmann

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

3296 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3297 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3298 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3299 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3300 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3301 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3302 de0ea66b Michael Hanselmann

3303 de0ea66b Michael Hanselmann
  @type fn: callable
3304 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3305 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3306 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3307 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3308 de0ea66b Michael Hanselmann
  @type timeout: float
3309 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3310 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3311 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3312 de0ea66b Michael Hanselmann
  @return: Return value of function
3313 de0ea66b Michael Hanselmann

3314 de0ea66b Michael Hanselmann
  """
3315 de0ea66b Michael Hanselmann
  assert callable(fn)
3316 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
3317 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
3318 de0ea66b Michael Hanselmann
3319 de0ea66b Michael Hanselmann
  if args is None:
3320 de0ea66b Michael Hanselmann
    args = []
3321 de0ea66b Michael Hanselmann
3322 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
3323 de0ea66b Michael Hanselmann
3324 de0ea66b Michael Hanselmann
  if callable(delay):
3325 de0ea66b Michael Hanselmann
    # External function to calculate delay
3326 de0ea66b Michael Hanselmann
    calc_delay = delay
3327 de0ea66b Michael Hanselmann
3328 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
3329 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
3330 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
3331 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
3332 de0ea66b Michael Hanselmann
3333 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
3334 de0ea66b Michael Hanselmann
    # Always use the remaining time
3335 de0ea66b Michael Hanselmann
    calc_delay = None
3336 de0ea66b Michael Hanselmann
3337 de0ea66b Michael Hanselmann
  else:
3338 de0ea66b Michael Hanselmann
    # Static delay
3339 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
3340 de0ea66b Michael Hanselmann
3341 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
3342 de0ea66b Michael Hanselmann
3343 de0ea66b Michael Hanselmann
  while True:
3344 506be7c5 Guido Trotter
    retry_args = []
3345 de0ea66b Michael Hanselmann
    try:
3346 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
3347 de0ea66b Michael Hanselmann
      return fn(*args)
3348 506be7c5 Guido Trotter
    except RetryAgain, err:
3349 506be7c5 Guido Trotter
      retry_args = err.args
3350 1b429e2a Iustin Pop
    except RetryTimeout:
3351 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
3352 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
3353 de0ea66b Michael Hanselmann
3354 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
3355 de0ea66b Michael Hanselmann
3356 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
3357 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
3358 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
3359 de0ea66b Michael Hanselmann
3360 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
3361 de0ea66b Michael Hanselmann
3362 de0ea66b Michael Hanselmann
    if calc_delay is None:
3363 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
3364 de0ea66b Michael Hanselmann
    else:
3365 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
3366 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
3367 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
3368 de0ea66b Michael Hanselmann
3369 de0ea66b Michael Hanselmann
3370 bdd5e420 Michael Hanselmann
def GetClosedTempfile(*args, **kwargs):
3371 bdd5e420 Michael Hanselmann
  """Creates a temporary file and returns its path.
3372 a55474c7 Michael Hanselmann

3373 bdd5e420 Michael Hanselmann
  """
3374 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3375 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3376 bdd5e420 Michael Hanselmann
  return path
3377 bdd5e420 Michael Hanselmann
3378 bdd5e420 Michael Hanselmann
3379 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3380 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3381 bdd5e420 Michael Hanselmann

3382 bdd5e420 Michael Hanselmann
  @type common_name: string
3383 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3384 a55474c7 Michael Hanselmann
  @type validity: int
3385 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3386 a55474c7 Michael Hanselmann

3387 a55474c7 Michael Hanselmann
  """
3388 bdd5e420 Michael Hanselmann
  # Create private and public key
3389 bdd5e420 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
3390 bdd5e420 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
3391 bdd5e420 Michael Hanselmann
3392 bdd5e420 Michael Hanselmann
  # Create self-signed certificate
3393 bdd5e420 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
3394 bdd5e420 Michael Hanselmann
  if common_name:
3395 bdd5e420 Michael Hanselmann
    cert.get_subject().CN = common_name
3396 bdd5e420 Michael Hanselmann
  cert.set_serial_number(1)
3397 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
3398 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
3399 bdd5e420 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
3400 bdd5e420 Michael Hanselmann
  cert.set_pubkey(key)
3401 bdd5e420 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
3402 bdd5e420 Michael Hanselmann
3403 bdd5e420 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
3404 bdd5e420 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3405 bdd5e420 Michael Hanselmann
3406 bdd5e420 Michael Hanselmann
  return (key_pem, cert_pem)
3407 bdd5e420 Michael Hanselmann
3408 bdd5e420 Michael Hanselmann
3409 600535f0 Manuel Franceschini
def GenerateSelfSignedSslCert(filename, common_name=constants.X509_CERT_CN,
3410 600535f0 Manuel Franceschini
                              validity=constants.X509_CERT_DEFAULT_VALIDITY):
3411 bdd5e420 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
3412 bdd5e420 Michael Hanselmann

3413 2ea65c7d Manuel Franceschini
  @type filename: str
3414 2ea65c7d Manuel Franceschini
  @param filename: path to write certificate to
3415 600535f0 Manuel Franceschini
  @type common_name: string
3416 600535f0 Manuel Franceschini
  @param common_name: commonName value
3417 600535f0 Manuel Franceschini
  @type validity: int
3418 600535f0 Manuel Franceschini
  @param validity: validity of certificate in number of days
3419 600535f0 Manuel Franceschini

3420 bdd5e420 Michael Hanselmann
  """
3421 600535f0 Manuel Franceschini
  # TODO: Investigate using the cluster name instead of X505_CERT_CN for
3422 600535f0 Manuel Franceschini
  # common_name, as cluster-renames are very seldom, and it'd be nice if RAPI
3423 600535f0 Manuel Franceschini
  # and node daemon certificates have the proper Subject/Issuer.
3424 600535f0 Manuel Franceschini
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(common_name,
3425 bdd5e420 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
3426 bdd5e420 Michael Hanselmann
3427 bdd5e420 Michael Hanselmann
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
3428 a55474c7 Michael Hanselmann
3429 a55474c7 Michael Hanselmann
3430 a87b4824 Michael Hanselmann
class FileLock(object):
3431 a87b4824 Michael Hanselmann
  """Utility class for file locks.
3432 a87b4824 Michael Hanselmann

3433 a87b4824 Michael Hanselmann
  """
3434 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3435 58885d79 Iustin Pop
    """Constructor for FileLock.
3436 58885d79 Iustin Pop

3437 b4478d34 Michael Hanselmann
    @type fd: file
3438 b4478d34 Michael Hanselmann
    @param fd: File object
3439 58885d79 Iustin Pop
    @type filename: str
3440 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3441 58885d79 Iustin Pop

3442 58885d79 Iustin Pop
    """
3443 b4478d34 Michael Hanselmann
    self.fd = fd
3444 a87b4824 Michael Hanselmann
    self.filename = filename
3445 b4478d34 Michael Hanselmann
3446 b4478d34 Michael Hanselmann
  @classmethod
3447 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3448 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3449 b4478d34 Michael Hanselmann

3450 b4478d34 Michael Hanselmann
    @type filename: string
3451 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3452 b4478d34 Michael Hanselmann

3453 b4478d34 Michael Hanselmann
    """
3454 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3455 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3456 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3457 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3458 b4478d34 Michael Hanselmann
               filename)
3459 a87b4824 Michael Hanselmann
3460 a87b4824 Michael Hanselmann
  def __del__(self):
3461 a87b4824 Michael Hanselmann
    self.Close()
3462 a87b4824 Michael Hanselmann
3463 a87b4824 Michael Hanselmann
  def Close(self):
3464 58885d79 Iustin Pop
    """Close the file and release the lock.
3465 58885d79 Iustin Pop

3466 58885d79 Iustin Pop
    """
3467 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3468 a87b4824 Michael Hanselmann
      self.fd.close()
3469 a87b4824 Michael Hanselmann
      self.fd = None
3470 a87b4824 Michael Hanselmann
3471 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3472 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3473 aa74b828 Michael Hanselmann

3474 aa74b828 Michael Hanselmann
    @type flag: int
3475 58885d79 Iustin Pop
    @param flag: operation flag
3476 aa74b828 Michael Hanselmann
    @type blocking: bool
3477 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3478 aa74b828 Michael Hanselmann
    @type timeout: None or float
3479 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3480 aa74b828 Michael Hanselmann
                    non-blocking mode).
3481 aa74b828 Michael Hanselmann
    @type errmsg: string
3482 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3483 aa74b828 Michael Hanselmann

3484 aa74b828 Michael Hanselmann
    """
3485 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
3486 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
3487 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
3488 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
3489 a87b4824 Michael Hanselmann
3490 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
3491 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
3492 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
3493 a87b4824 Michael Hanselmann
3494 cc4c9b91 Michael Hanselmann
    if timeout is None:
3495 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
3496 cc4c9b91 Michael Hanselmann
    else:
3497 cc4c9b91 Michael Hanselmann
      try:
3498 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
3499 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
3500 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
3501 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
3502 aa74b828 Michael Hanselmann
3503 cc4c9b91 Michael Hanselmann
  @staticmethod
3504 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
3505 cc4c9b91 Michael Hanselmann
    try:
3506 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
3507 cc4c9b91 Michael Hanselmann
    except IOError, err:
3508 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
3509 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
3510 31892b4c Michael Hanselmann
3511 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
3512 cc4c9b91 Michael Hanselmann
      raise
3513 aa74b828 Michael Hanselmann
3514 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
3515 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
3516 a87b4824 Michael Hanselmann

3517 58885d79 Iustin Pop
    @type blocking: boolean
3518 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3519 58885d79 Iustin Pop
        can lock the file or return immediately
3520 58885d79 Iustin Pop
    @type timeout: int or None
3521 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3522 58885d79 Iustin Pop
        (in blocking mode)
3523 58885d79 Iustin Pop

3524 a87b4824 Michael Hanselmann
    """
3525 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3526 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3527 a87b4824 Michael Hanselmann
3528 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3529 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3530 a87b4824 Michael Hanselmann

3531 58885d79 Iustin Pop
    @type blocking: boolean
3532 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3533 58885d79 Iustin Pop
        can lock the file or return immediately
3534 58885d79 Iustin Pop
    @type timeout: int or None
3535 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3536 58885d79 Iustin Pop
        (in blocking mode)
3537 58885d79 Iustin Pop

3538 a87b4824 Michael Hanselmann
    """
3539 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3540 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3541 a87b4824 Michael Hanselmann
3542 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3543 a87b4824 Michael Hanselmann
    """Unlocks the file.
3544 a87b4824 Michael Hanselmann

3545 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3546 58885d79 Iustin Pop
    operation::
3547 58885d79 Iustin Pop

3548 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3549 58885d79 Iustin Pop
      operations.
3550 58885d79 Iustin Pop

3551 58885d79 Iustin Pop
    @type blocking: boolean
3552 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3553 58885d79 Iustin Pop
        can lock the file or return immediately
3554 58885d79 Iustin Pop
    @type timeout: int or None
3555 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3556 58885d79 Iustin Pop
        (in blocking mode)
3557 a87b4824 Michael Hanselmann

3558 a87b4824 Michael Hanselmann
    """
3559 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3560 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3561 a87b4824 Michael Hanselmann
3562 a87b4824 Michael Hanselmann
3563 339be5a8 Michael Hanselmann
class LineSplitter:
3564 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3565 339be5a8 Michael Hanselmann

3566 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3567 339be5a8 Michael Hanselmann

3568 339be5a8 Michael Hanselmann
  """
3569 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3570 339be5a8 Michael Hanselmann
    """Initializes this class.
3571 339be5a8 Michael Hanselmann

3572 339be5a8 Michael Hanselmann
    @type line_fn: callable
3573 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3574 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3575 339be5a8 Michael Hanselmann

3576 339be5a8 Michael Hanselmann
    """
3577 339be5a8 Michael Hanselmann
    assert callable(line_fn)
3578 339be5a8 Michael Hanselmann
3579 339be5a8 Michael Hanselmann
    if args:
3580 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
3581 339be5a8 Michael Hanselmann
      self._line_fn = \
3582 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
3583 339be5a8 Michael Hanselmann
    else:
3584 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
3585 339be5a8 Michael Hanselmann
3586 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
3587 339be5a8 Michael Hanselmann
    self._buffer = ""
3588 339be5a8 Michael Hanselmann
3589 339be5a8 Michael Hanselmann
  def write(self, data):
3590 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
3591 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
3592 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
3593 339be5a8 Michael Hanselmann
3594 339be5a8 Michael Hanselmann
  def flush(self):
3595 339be5a8 Michael Hanselmann
    while self._lines:
3596 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
3597 339be5a8 Michael Hanselmann
3598 339be5a8 Michael Hanselmann
  def close(self):
3599 339be5a8 Michael Hanselmann
    self.flush()
3600 339be5a8 Michael Hanselmann
    if self._buffer:
3601 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
3602 339be5a8 Michael Hanselmann
3603 339be5a8 Michael Hanselmann
3604 451575de Guido Trotter
def SignalHandled(signums):
3605 451575de Guido Trotter
  """Signal Handled decoration.
3606 451575de Guido Trotter

3607 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3608 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3609 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3610 451575de Guido Trotter
  objects as values.
3611 451575de Guido Trotter

3612 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3613 451575de Guido Trotter
  with different handlers.
3614 451575de Guido Trotter

3615 451575de Guido Trotter
  @type signums: list
3616 451575de Guido Trotter
  @param signums: signals to intercept
3617 451575de Guido Trotter

3618 451575de Guido Trotter
  """
3619 451575de Guido Trotter
  def wrap(fn):
3620 451575de Guido Trotter
    def sig_function(*args, **kwargs):
3621 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
3622 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
3623 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
3624 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
3625 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
3626 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
3627 451575de Guido Trotter
      else:
3628 451575de Guido Trotter
        signal_handlers = {}
3629 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
3630 451575de Guido Trotter
      sighandler = SignalHandler(signums)
3631 451575de Guido Trotter
      try:
3632 451575de Guido Trotter
        for sig in signums:
3633 451575de Guido Trotter
          signal_handlers[sig] = sighandler
3634 451575de Guido Trotter
        return fn(*args, **kwargs)
3635 451575de Guido Trotter
      finally:
3636 451575de Guido Trotter
        sighandler.Reset()
3637 451575de Guido Trotter
    return sig_function
3638 451575de Guido Trotter
  return wrap
3639 451575de Guido Trotter
3640 451575de Guido Trotter
3641 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
3642 b9768937 Michael Hanselmann
  try:
3643 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
3644 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
3645 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
3646 b9768937 Michael Hanselmann
  except AttributeError:
3647 b9768937 Michael Hanselmann
    # Not supported
3648 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, _): # pylint: disable-msg=R0201
3649 b9768937 Michael Hanselmann
      return -1
3650 b9768937 Michael Hanselmann
  else:
3651 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
3652 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
3653 b9768937 Michael Hanselmann
3654 b9768937 Michael Hanselmann
  def __init__(self):
3655 b9768937 Michael Hanselmann
    """Initializes this class.
3656 b9768937 Michael Hanselmann

3657 b9768937 Michael Hanselmann
    """
3658 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
3659 b9768937 Michael Hanselmann
3660 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
3661 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
3662 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
3663 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
3664 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
3665 b9768937 Michael Hanselmann
3666 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
3667 b9768937 Michael Hanselmann
3668 b9768937 Michael Hanselmann
    # Utility functions
3669 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
3670 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
3671 b9768937 Michael Hanselmann
3672 b9768937 Michael Hanselmann
  def Reset(self):
3673 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
3674 b9768937 Michael Hanselmann

3675 b9768937 Michael Hanselmann
    """
3676 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3677 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3678 b9768937 Michael Hanselmann
      self._previous = None
3679 b9768937 Michael Hanselmann
3680 b9768937 Michael Hanselmann
  def Notify(self):
3681 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3682 b9768937 Michael Hanselmann

3683 b9768937 Michael Hanselmann
    """
3684 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3685 b9768937 Michael Hanselmann
3686 b9768937 Michael Hanselmann
  def __del__(self):
3687 b9768937 Michael Hanselmann
    """Called before object deletion.
3688 b9768937 Michael Hanselmann

3689 b9768937 Michael Hanselmann
    """
3690 b9768937 Michael Hanselmann
    self.Reset()
3691 b9768937 Michael Hanselmann
3692 b9768937 Michael Hanselmann
3693 de499029 Michael Hanselmann
class SignalHandler(object):
3694 de499029 Michael Hanselmann
  """Generic signal handler class.
3695 de499029 Michael Hanselmann

3696 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3697 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3698 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3699 58885d79 Iustin Pop
  signal was sent.
3700 58885d79 Iustin Pop

3701 58885d79 Iustin Pop
  @type signum: list
3702 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3703 58885d79 Iustin Pop
  @type called: boolean
3704 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3705 de499029 Michael Hanselmann

3706 de499029 Michael Hanselmann
  """
3707 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3708 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3709 de499029 Michael Hanselmann

3710 58885d79 Iustin Pop
    @type signum: int or list of ints
3711 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3712 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3713 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3714 de499029 Michael Hanselmann

3715 de499029 Michael Hanselmann
    """
3716 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
3717 92b61ec7 Michael Hanselmann
3718 6c52849e Guido Trotter
    self.signum = set(signum)
3719 de499029 Michael Hanselmann
    self.called = False
3720 de499029 Michael Hanselmann
3721 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
3722 b9768937 Michael Hanselmann
    self._wakeup = wakeup
3723 92b61ec7 Michael Hanselmann
3724 de499029 Michael Hanselmann
    self._previous = {}
3725 de499029 Michael Hanselmann
    try:
3726 de499029 Michael Hanselmann
      for signum in self.signum:
3727 de499029 Michael Hanselmann
        # Setup handler
3728 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3729 de499029 Michael Hanselmann
        try:
3730 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3731 de499029 Michael Hanselmann
        except:
3732 de499029 Michael Hanselmann
          # Restore previous handler
3733 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3734 de499029 Michael Hanselmann
          raise
3735 de499029 Michael Hanselmann
    except:
3736 de499029 Michael Hanselmann
      # Reset all handlers
3737 de499029 Michael Hanselmann
      self.Reset()
3738 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3739 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3740 de499029 Michael Hanselmann
      raise
3741 de499029 Michael Hanselmann
3742 de499029 Michael Hanselmann
  def __del__(self):
3743 de499029 Michael Hanselmann
    self.Reset()
3744 de499029 Michael Hanselmann
3745 de499029 Michael Hanselmann
  def Reset(self):
3746 de499029 Michael Hanselmann
    """Restore previous handler.
3747 de499029 Michael Hanselmann

3748 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3749 58885d79 Iustin Pop

3750 de499029 Michael Hanselmann
    """
3751 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3752 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3753 de499029 Michael Hanselmann
      # If successful, remove from dict
3754 de499029 Michael Hanselmann
      del self._previous[signum]
3755 de499029 Michael Hanselmann
3756 de499029 Michael Hanselmann
  def Clear(self):
3757 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3758 de499029 Michael Hanselmann

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

3761 de499029 Michael Hanselmann
    """
3762 de499029 Michael Hanselmann
    self.called = False
3763 de499029 Michael Hanselmann
3764 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3765 de499029 Michael Hanselmann
    """Actual signal handling function.
3766 de499029 Michael Hanselmann

3767 de499029 Michael Hanselmann
    """
3768 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3769 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3770 de499029 Michael Hanselmann
    self.called = True
3771 a2d2e1a7 Iustin Pop
3772 b9768937 Michael Hanselmann
    if self._wakeup:
3773 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
3774 b9768937 Michael Hanselmann
      self._wakeup.Notify()
3775 b9768937 Michael Hanselmann
3776 92b61ec7 Michael Hanselmann
    if self._handler_fn:
3777 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
3778 92b61ec7 Michael Hanselmann
3779 a2d2e1a7 Iustin Pop
3780 a2d2e1a7 Iustin Pop
class FieldSet(object):
3781 a2d2e1a7 Iustin Pop
  """A simple field set.
3782 a2d2e1a7 Iustin Pop

3783 a2d2e1a7 Iustin Pop
  Among the features are:
3784 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3785 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3786 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3787 a2d2e1a7 Iustin Pop

3788 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3789 a2d2e1a7 Iustin Pop

3790 a2d2e1a7 Iustin Pop
  """
3791 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3792 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3793 a2d2e1a7 Iustin Pop
3794 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3795 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3796 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3797 a2d2e1a7 Iustin Pop
3798 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3799 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3800 a2d2e1a7 Iustin Pop

3801 a2d2e1a7 Iustin Pop
    @type field: str
3802 a2d2e1a7 Iustin Pop
    @param field: the string to match
3803 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3804 a2d2e1a7 Iustin Pop

3805 a2d2e1a7 Iustin Pop
    """
3806 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3807 a2d2e1a7 Iustin Pop
      return m
3808 6c881c52 Iustin Pop
    return None
3809 a2d2e1a7 Iustin Pop
3810 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3811 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3812 a2d2e1a7 Iustin Pop

3813 a2d2e1a7 Iustin Pop
    @type items: list
3814 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3815 a2d2e1a7 Iustin Pop
    @rtype: list
3816 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3817 a2d2e1a7 Iustin Pop

3818 a2d2e1a7 Iustin Pop
    """
3819 a2d2e1a7 Iustin Pop
    return [val for val in items if not self.Matches(val)]