Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ c7406bbe

History | View | Annotate | Download (100 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 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 f93f2016 Michael Hanselmann
import struct
53 f93f2016 Michael Hanselmann
import IN
54 9c233417 Iustin Pop
55 9c233417 Iustin Pop
from cStringIO import StringIO
56 a8083063 Iustin Pop
57 7ffe8fba Carlos Valiente
try:
58 7ffe8fba Carlos Valiente
  from hashlib import sha1
59 7ffe8fba Carlos Valiente
except ImportError:
60 68857643 Michael Hanselmann
  import sha as sha1
61 7ffe8fba Carlos Valiente
62 4b6fa0bf Luca Bigliardi
try:
63 4b6fa0bf Luca Bigliardi
  import ctypes
64 4b6fa0bf Luca Bigliardi
except ImportError:
65 4b6fa0bf Luca Bigliardi
  ctypes = None
66 4b6fa0bf Luca Bigliardi
67 a8083063 Iustin Pop
from ganeti import errors
68 3aecd2c7 Iustin Pop
from ganeti import constants
69 a8083063 Iustin Pop
70 16abfbc2 Alexander Schreiber
71 a8083063 Iustin Pop
_locksheld = []
72 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
73 a8083063 Iustin Pop
74 e67bd559 Michael Hanselmann
debug_locks = False
75 58885d79 Iustin Pop
76 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
77 b74159ee Iustin Pop
no_fork = False
78 f362096f Iustin Pop
79 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
80 13998ef2 Michael Hanselmann
81 68857643 Michael Hanselmann
HEX_CHAR_RE = r"[a-zA-Z0-9]"
82 68857643 Michael Hanselmann
VALID_X509_SIGNATURE_SALT = re.compile("^%s+$" % HEX_CHAR_RE, re.S)
83 68857643 Michael Hanselmann
X509_SIGNATURE = re.compile(r"^%s:\s*(?P<salt>%s+)/(?P<sign>%s+)$" %
84 68857643 Michael Hanselmann
                            (re.escape(constants.X509_CERT_SIGNATURE_HEADER),
85 68857643 Michael Hanselmann
                             HEX_CHAR_RE, HEX_CHAR_RE),
86 68857643 Michael Hanselmann
                            re.S | re.I)
87 68857643 Michael Hanselmann
88 f93f2016 Michael Hanselmann
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
89 f93f2016 Michael Hanselmann
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
90 f93f2016 Michael Hanselmann
#
91 f93f2016 Michael Hanselmann
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
92 f93f2016 Michael Hanselmann
# pid_t to "int".
93 f93f2016 Michael Hanselmann
#
94 f93f2016 Michael Hanselmann
# IEEE Std 1003.1-2008:
95 f93f2016 Michael Hanselmann
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
96 f93f2016 Michael Hanselmann
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
97 f93f2016 Michael Hanselmann
_STRUCT_UCRED = "iII"
98 f93f2016 Michael Hanselmann
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
99 f93f2016 Michael Hanselmann
100 24d70417 Michael Hanselmann
# Certificate verification results
101 24d70417 Michael Hanselmann
(CERT_WARNING,
102 24d70417 Michael Hanselmann
 CERT_ERROR) = range(1, 3)
103 24d70417 Michael Hanselmann
104 4b6fa0bf Luca Bigliardi
# Flags for mlockall() (from bits/mman.h)
105 4b6fa0bf Luca Bigliardi
_MCL_CURRENT = 1
106 4b6fa0bf Luca Bigliardi
_MCL_FUTURE = 2
107 4b6fa0bf Luca Bigliardi
108 7c0d6283 Michael Hanselmann
109 a8083063 Iustin Pop
class RunResult(object):
110 58885d79 Iustin Pop
  """Holds the result of running external programs.
111 58885d79 Iustin Pop

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

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

154 a8083063 Iustin Pop
    """
155 a8083063 Iustin Pop
    return self.stdout + self.stderr
156 a8083063 Iustin Pop
157 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
158 a8083063 Iustin Pop
159 a8083063 Iustin Pop
160 bb3776b4 Michael Hanselmann
def _BuildCmdEnvironment(env, reset):
161 c1dd99d4 Michael Hanselmann
  """Builds the environment for an external program.
162 c1dd99d4 Michael Hanselmann

163 c1dd99d4 Michael Hanselmann
  """
164 bb3776b4 Michael Hanselmann
  if reset:
165 bb3776b4 Michael Hanselmann
    cmd_env = {}
166 bb3776b4 Michael Hanselmann
  else:
167 bb3776b4 Michael Hanselmann
    cmd_env = os.environ.copy()
168 bb3776b4 Michael Hanselmann
    cmd_env["LC_ALL"] = "C"
169 bb3776b4 Michael Hanselmann
170 c1dd99d4 Michael Hanselmann
  if env is not None:
171 c1dd99d4 Michael Hanselmann
    cmd_env.update(env)
172 bb3776b4 Michael Hanselmann
173 c1dd99d4 Michael Hanselmann
  return cmd_env
174 c1dd99d4 Michael Hanselmann
175 c1dd99d4 Michael Hanselmann
176 bb3776b4 Michael Hanselmann
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False):
177 a8083063 Iustin Pop
  """Execute a (shell) command.
178 a8083063 Iustin Pop

179 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
180 a8083063 Iustin Pop
  closed.
181 a8083063 Iustin Pop

182 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
183 36117c2b Iustin Pop
  @param cmd: Command to run
184 2557ff82 Guido Trotter
  @type env: dict
185 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
186 36117c2b Iustin Pop
  @type output: str
187 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
188 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
189 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
190 8797df43 Iustin Pop
  @type cwd: string
191 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
192 8797df43 Iustin Pop
      directory for the command; the default will be /
193 bf4daac9 Guido Trotter
  @type reset_env: boolean
194 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
195 36117c2b Iustin Pop
  @rtype: L{RunResult}
196 58885d79 Iustin Pop
  @return: RunResult instance
197 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
198 a8083063 Iustin Pop

199 a8083063 Iustin Pop
  """
200 b74159ee Iustin Pop
  if no_fork:
201 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
202 b74159ee Iustin Pop
203 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
204 c1dd99d4 Michael Hanselmann
    strcmd = cmd
205 c1dd99d4 Michael Hanselmann
    shell = True
206 c1dd99d4 Michael Hanselmann
  else:
207 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
208 c1dd99d4 Michael Hanselmann
    strcmd = ShellQuoteArgs(cmd)
209 113b55aa Iustin Pop
    shell = False
210 c1dd99d4 Michael Hanselmann
211 c1dd99d4 Michael Hanselmann
  if output:
212 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s, output file '%s'", strcmd, output)
213 113b55aa Iustin Pop
  else:
214 c1dd99d4 Michael Hanselmann
    logging.debug("RunCmd %s", strcmd)
215 2557ff82 Guido Trotter
216 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, reset_env)
217 2557ff82 Guido Trotter
218 c803b052 Iustin Pop
  try:
219 c803b052 Iustin Pop
    if output is None:
220 c803b052 Iustin Pop
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
221 c803b052 Iustin Pop
    else:
222 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
223 c803b052 Iustin Pop
      out = err = ""
224 c803b052 Iustin Pop
  except OSError, err:
225 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
226 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
227 c803b052 Iustin Pop
                               (strcmd, err))
228 c803b052 Iustin Pop
    else:
229 c803b052 Iustin Pop
      raise
230 36117c2b Iustin Pop
231 36117c2b Iustin Pop
  if status >= 0:
232 36117c2b Iustin Pop
    exitcode = status
233 36117c2b Iustin Pop
    signal_ = None
234 36117c2b Iustin Pop
  else:
235 36117c2b Iustin Pop
    exitcode = None
236 36117c2b Iustin Pop
    signal_ = -status
237 36117c2b Iustin Pop
238 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
239 36117c2b Iustin Pop
240 ae59efea Michael Hanselmann
241 c1dd99d4 Michael Hanselmann
def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
242 c1dd99d4 Michael Hanselmann
                pidfile=None):
243 c1dd99d4 Michael Hanselmann
  """Start a daemon process after forking twice.
244 c1dd99d4 Michael Hanselmann

245 c1dd99d4 Michael Hanselmann
  @type cmd: string or list
246 c1dd99d4 Michael Hanselmann
  @param cmd: Command to run
247 c1dd99d4 Michael Hanselmann
  @type env: dict
248 c1dd99d4 Michael Hanselmann
  @param env: Additional environment variables
249 c1dd99d4 Michael Hanselmann
  @type cwd: string
250 c1dd99d4 Michael Hanselmann
  @param cwd: Working directory for the program
251 c1dd99d4 Michael Hanselmann
  @type output: string
252 c1dd99d4 Michael Hanselmann
  @param output: Path to file in which to save the output
253 c1dd99d4 Michael Hanselmann
  @type output_fd: int
254 c1dd99d4 Michael Hanselmann
  @param output_fd: File descriptor for output
255 c1dd99d4 Michael Hanselmann
  @type pidfile: string
256 c1dd99d4 Michael Hanselmann
  @param pidfile: Process ID file
257 c1dd99d4 Michael Hanselmann
  @rtype: int
258 c1dd99d4 Michael Hanselmann
  @return: Daemon process ID
259 c1dd99d4 Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
260 c1dd99d4 Michael Hanselmann

261 c1dd99d4 Michael Hanselmann
  """
262 c1dd99d4 Michael Hanselmann
  if no_fork:
263 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("utils.StartDaemon() called with fork()"
264 c1dd99d4 Michael Hanselmann
                                 " disabled")
265 c1dd99d4 Michael Hanselmann
266 c1dd99d4 Michael Hanselmann
  if output and not (bool(output) ^ (output_fd is not None)):
267 c1dd99d4 Michael Hanselmann
    raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be"
268 c1dd99d4 Michael Hanselmann
                                 " specified")
269 c1dd99d4 Michael Hanselmann
270 c1dd99d4 Michael Hanselmann
  if isinstance(cmd, basestring):
271 c1dd99d4 Michael Hanselmann
    cmd = ["/bin/sh", "-c", cmd]
272 c1dd99d4 Michael Hanselmann
273 c1dd99d4 Michael Hanselmann
  strcmd = ShellQuoteArgs(cmd)
274 c1dd99d4 Michael Hanselmann
275 c1dd99d4 Michael Hanselmann
  if output:
276 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
277 c1dd99d4 Michael Hanselmann
  else:
278 c1dd99d4 Michael Hanselmann
    logging.debug("StartDaemon %s", strcmd)
279 c1dd99d4 Michael Hanselmann
280 bb3776b4 Michael Hanselmann
  cmd_env = _BuildCmdEnvironment(env, False)
281 c1dd99d4 Michael Hanselmann
282 c1dd99d4 Michael Hanselmann
  # Create pipe for sending PID back
283 c1dd99d4 Michael Hanselmann
  (pidpipe_read, pidpipe_write) = os.pipe()
284 c1dd99d4 Michael Hanselmann
  try:
285 c1dd99d4 Michael Hanselmann
    try:
286 c1dd99d4 Michael Hanselmann
      # Create pipe for sending error messages
287 c1dd99d4 Michael Hanselmann
      (errpipe_read, errpipe_write) = os.pipe()
288 c1dd99d4 Michael Hanselmann
      try:
289 c1dd99d4 Michael Hanselmann
        try:
290 c1dd99d4 Michael Hanselmann
          # First fork
291 c1dd99d4 Michael Hanselmann
          pid = os.fork()
292 c1dd99d4 Michael Hanselmann
          if pid == 0:
293 c1dd99d4 Michael Hanselmann
            try:
294 c1dd99d4 Michael Hanselmann
              # Child process, won't return
295 e0bb431e Michael Hanselmann
              _StartDaemonChild(errpipe_read, errpipe_write,
296 e0bb431e Michael Hanselmann
                                pidpipe_read, pidpipe_write,
297 e0bb431e Michael Hanselmann
                                cmd, cmd_env, cwd,
298 e0bb431e Michael Hanselmann
                                output, output_fd, pidfile)
299 c1dd99d4 Michael Hanselmann
            finally:
300 c1dd99d4 Michael Hanselmann
              # Well, maybe child process failed
301 e0bb431e Michael Hanselmann
              os._exit(1) # pylint: disable-msg=W0212
302 c1dd99d4 Michael Hanselmann
        finally:
303 c1dd99d4 Michael Hanselmann
          _CloseFDNoErr(errpipe_write)
304 c1dd99d4 Michael Hanselmann
305 c1dd99d4 Michael Hanselmann
        # Wait for daemon to be started (or an error message to arrive) and read
306 c1dd99d4 Michael Hanselmann
        # up to 100 KB as an error message
307 c1dd99d4 Michael Hanselmann
        errormsg = RetryOnSignal(os.read, errpipe_read, 100 * 1024)
308 c1dd99d4 Michael Hanselmann
      finally:
309 c1dd99d4 Michael Hanselmann
        _CloseFDNoErr(errpipe_read)
310 c1dd99d4 Michael Hanselmann
    finally:
311 c1dd99d4 Michael Hanselmann
      _CloseFDNoErr(pidpipe_write)
312 c1dd99d4 Michael Hanselmann
313 c1dd99d4 Michael Hanselmann
    # Read up to 128 bytes for PID
314 c1dd99d4 Michael Hanselmann
    pidtext = RetryOnSignal(os.read, pidpipe_read, 128)
315 c1dd99d4 Michael Hanselmann
  finally:
316 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
317 c1dd99d4 Michael Hanselmann
318 c1dd99d4 Michael Hanselmann
  # Try to avoid zombies by waiting for child process
319 c1dd99d4 Michael Hanselmann
  try:
320 c1dd99d4 Michael Hanselmann
    os.waitpid(pid, 0)
321 c1dd99d4 Michael Hanselmann
  except OSError:
322 c1dd99d4 Michael Hanselmann
    pass
323 c1dd99d4 Michael Hanselmann
324 c1dd99d4 Michael Hanselmann
  if errormsg:
325 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error when starting daemon process: %r" %
326 c1dd99d4 Michael Hanselmann
                             errormsg)
327 c1dd99d4 Michael Hanselmann
328 c1dd99d4 Michael Hanselmann
  try:
329 c1dd99d4 Michael Hanselmann
    return int(pidtext)
330 c1dd99d4 Michael Hanselmann
  except (ValueError, TypeError), err:
331 c1dd99d4 Michael Hanselmann
    raise errors.OpExecError("Error while trying to parse PID %r: %s" %
332 c1dd99d4 Michael Hanselmann
                             (pidtext, err))
333 c1dd99d4 Michael Hanselmann
334 c1dd99d4 Michael Hanselmann
335 e0bb431e Michael Hanselmann
def _StartDaemonChild(errpipe_read, errpipe_write,
336 e0bb431e Michael Hanselmann
                      pidpipe_read, pidpipe_write,
337 e0bb431e Michael Hanselmann
                      args, env, cwd,
338 e0bb431e Michael Hanselmann
                      output, fd_output, pidfile):
339 c1dd99d4 Michael Hanselmann
  """Child process for starting daemon.
340 c1dd99d4 Michael Hanselmann

341 c1dd99d4 Michael Hanselmann
  """
342 c1dd99d4 Michael Hanselmann
  try:
343 c1dd99d4 Michael Hanselmann
    # Close parent's side
344 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(errpipe_read)
345 c1dd99d4 Michael Hanselmann
    _CloseFDNoErr(pidpipe_read)
346 c1dd99d4 Michael Hanselmann
347 c1dd99d4 Michael Hanselmann
    # First child process
348 c1dd99d4 Michael Hanselmann
    os.chdir("/")
349 c1dd99d4 Michael Hanselmann
    os.umask(077)
350 c1dd99d4 Michael Hanselmann
    os.setsid()
351 c1dd99d4 Michael Hanselmann
352 c1dd99d4 Michael Hanselmann
    # And fork for the second time
353 c1dd99d4 Michael Hanselmann
    pid = os.fork()
354 c1dd99d4 Michael Hanselmann
    if pid != 0:
355 c1dd99d4 Michael Hanselmann
      # Exit first child process
356 c1dd99d4 Michael Hanselmann
      os._exit(0) # pylint: disable-msg=W0212
357 c1dd99d4 Michael Hanselmann
358 c1dd99d4 Michael Hanselmann
    # Make sure pipe is closed on execv* (and thereby notifies original process)
359 c1dd99d4 Michael Hanselmann
    SetCloseOnExecFlag(errpipe_write, True)
360 c1dd99d4 Michael Hanselmann
361 c1dd99d4 Michael Hanselmann
    # List of file descriptors to be left open
362 c1dd99d4 Michael Hanselmann
    noclose_fds = [errpipe_write]
363 c1dd99d4 Michael Hanselmann
364 c1dd99d4 Michael Hanselmann
    # Open PID file
365 c1dd99d4 Michael Hanselmann
    if pidfile:
366 c1dd99d4 Michael Hanselmann
      try:
367 c1dd99d4 Michael Hanselmann
        # TODO: Atomic replace with another locked file instead of writing into
368 c1dd99d4 Michael Hanselmann
        # it after creating
369 c1dd99d4 Michael Hanselmann
        fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
370 c1dd99d4 Michael Hanselmann
371 c1dd99d4 Michael Hanselmann
        # Lock the PID file (and fail if not possible to do so). Any code
372 c1dd99d4 Michael Hanselmann
        # wanting to send a signal to the daemon should try to lock the PID
373 c1dd99d4 Michael Hanselmann
        # file before reading it. If acquiring the lock succeeds, the daemon is
374 c1dd99d4 Michael Hanselmann
        # no longer running and the signal should not be sent.
375 c1dd99d4 Michael Hanselmann
        LockFile(fd_pidfile)
376 c1dd99d4 Michael Hanselmann
377 c1dd99d4 Michael Hanselmann
        os.write(fd_pidfile, "%d\n" % os.getpid())
378 c1dd99d4 Michael Hanselmann
      except Exception, err:
379 c1dd99d4 Michael Hanselmann
        raise Exception("Creating and locking PID file failed: %s" % err)
380 c1dd99d4 Michael Hanselmann
381 c1dd99d4 Michael Hanselmann
      # Keeping the file open to hold the lock
382 c1dd99d4 Michael Hanselmann
      noclose_fds.append(fd_pidfile)
383 c1dd99d4 Michael Hanselmann
384 c1dd99d4 Michael Hanselmann
      SetCloseOnExecFlag(fd_pidfile, False)
385 c1dd99d4 Michael Hanselmann
    else:
386 c1dd99d4 Michael Hanselmann
      fd_pidfile = None
387 c1dd99d4 Michael Hanselmann
388 c1dd99d4 Michael Hanselmann
    # Open /dev/null
389 c1dd99d4 Michael Hanselmann
    fd_devnull = os.open(os.devnull, os.O_RDWR)
390 c1dd99d4 Michael Hanselmann
391 c1dd99d4 Michael Hanselmann
    assert not output or (bool(output) ^ (fd_output is not None))
392 c1dd99d4 Michael Hanselmann
393 c1dd99d4 Michael Hanselmann
    if fd_output is not None:
394 c1dd99d4 Michael Hanselmann
      pass
395 c1dd99d4 Michael Hanselmann
    elif output:
396 c1dd99d4 Michael Hanselmann
      # Open output file
397 c1dd99d4 Michael Hanselmann
      try:
398 c1dd99d4 Michael Hanselmann
        # TODO: Implement flag to set append=yes/no
399 c1dd99d4 Michael Hanselmann
        fd_output = os.open(output, os.O_WRONLY | os.O_CREAT, 0600)
400 c1dd99d4 Michael Hanselmann
      except EnvironmentError, err:
401 c1dd99d4 Michael Hanselmann
        raise Exception("Opening output file failed: %s" % err)
402 c1dd99d4 Michael Hanselmann
    else:
403 c1dd99d4 Michael Hanselmann
      fd_output = fd_devnull
404 c1dd99d4 Michael Hanselmann
405 c1dd99d4 Michael Hanselmann
    # Redirect standard I/O
406 c1dd99d4 Michael Hanselmann
    os.dup2(fd_devnull, 0)
407 c1dd99d4 Michael Hanselmann
    os.dup2(fd_output, 1)
408 c1dd99d4 Michael Hanselmann
    os.dup2(fd_output, 2)
409 c1dd99d4 Michael Hanselmann
410 c1dd99d4 Michael Hanselmann
    # Send daemon PID to parent
411 c1dd99d4 Michael Hanselmann
    RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
412 c1dd99d4 Michael Hanselmann
413 c1dd99d4 Michael Hanselmann
    # Close all file descriptors except stdio and error message pipe
414 c1dd99d4 Michael Hanselmann
    CloseFDs(noclose_fds=noclose_fds)
415 c1dd99d4 Michael Hanselmann
416 c1dd99d4 Michael Hanselmann
    # Change working directory
417 c1dd99d4 Michael Hanselmann
    os.chdir(cwd)
418 c1dd99d4 Michael Hanselmann
419 c1dd99d4 Michael Hanselmann
    if env is None:
420 c1dd99d4 Michael Hanselmann
      os.execvp(args[0], args)
421 c1dd99d4 Michael Hanselmann
    else:
422 c1dd99d4 Michael Hanselmann
      os.execvpe(args[0], args, env)
423 c1dd99d4 Michael Hanselmann
  except: # pylint: disable-msg=W0702
424 c1dd99d4 Michael Hanselmann
    try:
425 c1dd99d4 Michael Hanselmann
      # Report errors to original process
426 c1dd99d4 Michael Hanselmann
      buf = str(sys.exc_info()[1])
427 c1dd99d4 Michael Hanselmann
428 c1dd99d4 Michael Hanselmann
      RetryOnSignal(os.write, errpipe_write, buf)
429 c1dd99d4 Michael Hanselmann
    except: # pylint: disable-msg=W0702
430 c1dd99d4 Michael Hanselmann
      # Ignore errors in error handling
431 c1dd99d4 Michael Hanselmann
      pass
432 c1dd99d4 Michael Hanselmann
433 c1dd99d4 Michael Hanselmann
  os._exit(1) # pylint: disable-msg=W0212
434 c1dd99d4 Michael Hanselmann
435 c1dd99d4 Michael Hanselmann
436 8797df43 Iustin Pop
def _RunCmdPipe(cmd, env, via_shell, cwd):
437 36117c2b Iustin Pop
  """Run a command and return its output.
438 36117c2b Iustin Pop

439 36117c2b Iustin Pop
  @type  cmd: string or list
440 36117c2b Iustin Pop
  @param cmd: Command to run
441 36117c2b Iustin Pop
  @type env: dict
442 36117c2b Iustin Pop
  @param env: The environment to use
443 36117c2b Iustin Pop
  @type via_shell: bool
444 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
445 8797df43 Iustin Pop
  @type cwd: string
446 8797df43 Iustin Pop
  @param cwd: the working directory for the program
447 36117c2b Iustin Pop
  @rtype: tuple
448 36117c2b Iustin Pop
  @return: (out, err, status)
449 36117c2b Iustin Pop

450 36117c2b Iustin Pop
  """
451 9c233417 Iustin Pop
  poller = select.poll()
452 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
453 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
454 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
455 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
456 8797df43 Iustin Pop
                           close_fds=True, env=env,
457 8797df43 Iustin Pop
                           cwd=cwd)
458 113b55aa Iustin Pop
459 113b55aa Iustin Pop
  child.stdin.close()
460 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
461 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
462 9c233417 Iustin Pop
  out = StringIO()
463 9c233417 Iustin Pop
  err = StringIO()
464 9c233417 Iustin Pop
  fdmap = {
465 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
466 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
467 9c233417 Iustin Pop
    }
468 9c233417 Iustin Pop
  for fd in fdmap:
469 287a1740 Michael Hanselmann
    SetNonblockFlag(fd, True)
470 9c233417 Iustin Pop
471 9c233417 Iustin Pop
  while fdmap:
472 edcb5d9e Michael Hanselmann
    pollresult = RetryOnSignal(poller.poll)
473 bf988c29 Guido Trotter
474 bf988c29 Guido Trotter
    for fd, event in pollresult:
475 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
476 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
477 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
478 9c233417 Iustin Pop
        if not data:
479 9c233417 Iustin Pop
          poller.unregister(fd)
480 9c233417 Iustin Pop
          del fdmap[fd]
481 9c233417 Iustin Pop
          continue
482 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
483 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
484 9c233417 Iustin Pop
          event & select.POLLERR):
485 9c233417 Iustin Pop
        poller.unregister(fd)
486 9c233417 Iustin Pop
        del fdmap[fd]
487 9c233417 Iustin Pop
488 9c233417 Iustin Pop
  out = out.getvalue()
489 9c233417 Iustin Pop
  err = err.getvalue()
490 a8083063 Iustin Pop
491 a8083063 Iustin Pop
  status = child.wait()
492 36117c2b Iustin Pop
  return out, err, status
493 a8083063 Iustin Pop
494 36117c2b Iustin Pop
495 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
496 36117c2b Iustin Pop
  """Run a command and save its output to a file.
497 36117c2b Iustin Pop

498 36117c2b Iustin Pop
  @type  cmd: string or list
499 36117c2b Iustin Pop
  @param cmd: Command to run
500 36117c2b Iustin Pop
  @type env: dict
501 36117c2b Iustin Pop
  @param env: The environment to use
502 36117c2b Iustin Pop
  @type via_shell: bool
503 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
504 36117c2b Iustin Pop
  @type output: str
505 36117c2b Iustin Pop
  @param output: the filename in which to save the output
506 8797df43 Iustin Pop
  @type cwd: string
507 8797df43 Iustin Pop
  @param cwd: the working directory for the program
508 36117c2b Iustin Pop
  @rtype: int
509 36117c2b Iustin Pop
  @return: the exit status
510 36117c2b Iustin Pop

511 36117c2b Iustin Pop
  """
512 36117c2b Iustin Pop
  fh = open(output, "a")
513 36117c2b Iustin Pop
  try:
514 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
515 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
516 36117c2b Iustin Pop
                             stdout=fh,
517 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
518 8797df43 Iustin Pop
                             close_fds=True, env=env,
519 8797df43 Iustin Pop
                             cwd=cwd)
520 36117c2b Iustin Pop
521 36117c2b Iustin Pop
    child.stdin.close()
522 36117c2b Iustin Pop
    status = child.wait()
523 36117c2b Iustin Pop
  finally:
524 36117c2b Iustin Pop
    fh.close()
525 36117c2b Iustin Pop
  return status
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
528 73027ed2 Michael Hanselmann
def SetCloseOnExecFlag(fd, enable):
529 73027ed2 Michael Hanselmann
  """Sets or unsets the close-on-exec flag on a file descriptor.
530 73027ed2 Michael Hanselmann

531 73027ed2 Michael Hanselmann
  @type fd: int
532 73027ed2 Michael Hanselmann
  @param fd: File descriptor
533 73027ed2 Michael Hanselmann
  @type enable: bool
534 73027ed2 Michael Hanselmann
  @param enable: Whether to set or unset it.
535 73027ed2 Michael Hanselmann

536 73027ed2 Michael Hanselmann
  """
537 73027ed2 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
538 73027ed2 Michael Hanselmann
539 73027ed2 Michael Hanselmann
  if enable:
540 73027ed2 Michael Hanselmann
    flags |= fcntl.FD_CLOEXEC
541 73027ed2 Michael Hanselmann
  else:
542 73027ed2 Michael Hanselmann
    flags &= ~fcntl.FD_CLOEXEC
543 73027ed2 Michael Hanselmann
544 73027ed2 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
545 73027ed2 Michael Hanselmann
546 73027ed2 Michael Hanselmann
547 287a1740 Michael Hanselmann
def SetNonblockFlag(fd, enable):
548 287a1740 Michael Hanselmann
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
549 287a1740 Michael Hanselmann

550 287a1740 Michael Hanselmann
  @type fd: int
551 287a1740 Michael Hanselmann
  @param fd: File descriptor
552 287a1740 Michael Hanselmann
  @type enable: bool
553 287a1740 Michael Hanselmann
  @param enable: Whether to set or unset it
554 287a1740 Michael Hanselmann

555 287a1740 Michael Hanselmann
  """
556 287a1740 Michael Hanselmann
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
557 287a1740 Michael Hanselmann
558 287a1740 Michael Hanselmann
  if enable:
559 287a1740 Michael Hanselmann
    flags |= os.O_NONBLOCK
560 287a1740 Michael Hanselmann
  else:
561 287a1740 Michael Hanselmann
    flags &= ~os.O_NONBLOCK
562 287a1740 Michael Hanselmann
563 287a1740 Michael Hanselmann
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
564 287a1740 Michael Hanselmann
565 287a1740 Michael Hanselmann
566 edcb5d9e Michael Hanselmann
def RetryOnSignal(fn, *args, **kwargs):
567 edcb5d9e Michael Hanselmann
  """Calls a function again if it failed due to EINTR.
568 edcb5d9e Michael Hanselmann

569 edcb5d9e Michael Hanselmann
  """
570 edcb5d9e Michael Hanselmann
  while True:
571 edcb5d9e Michael Hanselmann
    try:
572 edcb5d9e Michael Hanselmann
      return fn(*args, **kwargs)
573 c7406bbe Guido Trotter
    except (EnvironmentError, socket.error), err:
574 edcb5d9e Michael Hanselmann
      if err.errno != errno.EINTR:
575 edcb5d9e Michael Hanselmann
        raise
576 edcb5d9e Michael Hanselmann
    except select.error, err:
577 edcb5d9e Michael Hanselmann
      if not (err.args and err.args[0] == errno.EINTR):
578 edcb5d9e Michael Hanselmann
        raise
579 edcb5d9e Michael Hanselmann
580 edcb5d9e Michael Hanselmann
581 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
582 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
583 6bb65e3a Guido Trotter

584 6bb65e3a Guido Trotter
  @type dir_name: string
585 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
586 6bb65e3a Guido Trotter
  @type env: dict
587 6bb65e3a Guido Trotter
  @param env: The environment to use
588 6bb65e3a Guido Trotter
  @type reset_env: boolean
589 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
590 6bb65e3a Guido Trotter
  @rtype: list of tuples
591 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
592 6bb65e3a Guido Trotter

593 6bb65e3a Guido Trotter
  """
594 6bb65e3a Guido Trotter
  rr = []
595 6bb65e3a Guido Trotter
596 6bb65e3a Guido Trotter
  try:
597 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
598 6bb65e3a Guido Trotter
  except OSError, err:
599 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
600 6bb65e3a Guido Trotter
    return rr
601 6bb65e3a Guido Trotter
602 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
603 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
604 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
605 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
606 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
607 6bb65e3a Guido Trotter
    else:
608 6bb65e3a Guido Trotter
      try:
609 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
610 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
611 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
612 6bb65e3a Guido Trotter
      else:
613 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
614 6bb65e3a Guido Trotter
615 6bb65e3a Guido Trotter
  return rr
616 6bb65e3a Guido Trotter
617 6bb65e3a Guido Trotter
618 f93f2016 Michael Hanselmann
def GetSocketCredentials(sock):
619 f93f2016 Michael Hanselmann
  """Returns the credentials of the foreign process connected to a socket.
620 f93f2016 Michael Hanselmann

621 f93f2016 Michael Hanselmann
  @param sock: Unix socket
622 f93f2016 Michael Hanselmann
  @rtype: tuple; (number, number, number)
623 f93f2016 Michael Hanselmann
  @return: The PID, UID and GID of the connected foreign process.
624 f93f2016 Michael Hanselmann

625 f93f2016 Michael Hanselmann
  """
626 f93f2016 Michael Hanselmann
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
627 f93f2016 Michael Hanselmann
                             _STRUCT_UCRED_SIZE)
628 f93f2016 Michael Hanselmann
  return struct.unpack(_STRUCT_UCRED, peercred)
629 f93f2016 Michael Hanselmann
630 f93f2016 Michael Hanselmann
631 a8083063 Iustin Pop
def RemoveFile(filename):
632 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
633 a8083063 Iustin Pop

634 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
635 a8083063 Iustin Pop
  errors are passed.
636 a8083063 Iustin Pop

637 58885d79 Iustin Pop
  @type filename: str
638 58885d79 Iustin Pop
  @param filename: the file to be removed
639 58885d79 Iustin Pop

640 a8083063 Iustin Pop
  """
641 a8083063 Iustin Pop
  try:
642 a8083063 Iustin Pop
    os.unlink(filename)
643 a8083063 Iustin Pop
  except OSError, err:
644 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
645 a8083063 Iustin Pop
      raise
646 a8083063 Iustin Pop
647 a8083063 Iustin Pop
648 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
649 6e797216 Michael Hanselmann
  """Renames a file.
650 6e797216 Michael Hanselmann

651 6e797216 Michael Hanselmann
  @type old: string
652 6e797216 Michael Hanselmann
  @param old: Original path
653 6e797216 Michael Hanselmann
  @type new: string
654 6e797216 Michael Hanselmann
  @param new: New path
655 6e797216 Michael Hanselmann
  @type mkdir: bool
656 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
657 6e797216 Michael Hanselmann
  @type mkdir_mode: int
658 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
659 6e797216 Michael Hanselmann

660 6e797216 Michael Hanselmann
  """
661 6e797216 Michael Hanselmann
  try:
662 6e797216 Michael Hanselmann
    return os.rename(old, new)
663 6e797216 Michael Hanselmann
  except OSError, err:
664 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
665 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
666 6e797216 Michael Hanselmann
    # as efficient.
667 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
668 6e797216 Michael Hanselmann
      # Create directory and try again
669 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
670 a426508d Michael Hanselmann
671 6e797216 Michael Hanselmann
      return os.rename(old, new)
672 a426508d Michael Hanselmann
673 6e797216 Michael Hanselmann
    raise
674 6e797216 Michael Hanselmann
675 6e797216 Michael Hanselmann
676 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
677 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
678 76e5f8b5 Michael Hanselmann

679 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
680 76e5f8b5 Michael Hanselmann
  before Python 2.5.
681 76e5f8b5 Michael Hanselmann

682 76e5f8b5 Michael Hanselmann
  """
683 76e5f8b5 Michael Hanselmann
  try:
684 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
685 76e5f8b5 Michael Hanselmann
  except OSError, err:
686 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
687 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
688 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
689 76e5f8b5 Michael Hanselmann
      raise
690 76e5f8b5 Michael Hanselmann
691 76e5f8b5 Michael Hanselmann
692 055f822b Michael Hanselmann
def ResetTempfileModule():
693 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
694 055f822b Michael Hanselmann

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

701 055f822b Michael Hanselmann
  """
702 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
703 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
704 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
705 055f822b Michael Hanselmann
    try:
706 055f822b Michael Hanselmann
      # Reset random name generator
707 055f822b Michael Hanselmann
      tempfile._name_sequence = None
708 055f822b Michael Hanselmann
    finally:
709 055f822b Michael Hanselmann
      tempfile._once_lock.release()
710 055f822b Michael Hanselmann
  else:
711 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
712 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
713 055f822b Michael Hanselmann
714 055f822b Michael Hanselmann
715 a8083063 Iustin Pop
def _FingerprintFile(filename):
716 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
717 a8083063 Iustin Pop

718 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
719 a8083063 Iustin Pop
  instead.
720 a8083063 Iustin Pop

721 58885d79 Iustin Pop
  @type filename: str
722 58885d79 Iustin Pop
  @param filename: the filename to checksum
723 58885d79 Iustin Pop
  @rtype: str
724 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
725 58885d79 Iustin Pop
      of the file
726 a8083063 Iustin Pop

727 a8083063 Iustin Pop
  """
728 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
729 a8083063 Iustin Pop
    return None
730 a8083063 Iustin Pop
731 a8083063 Iustin Pop
  f = open(filename)
732 a8083063 Iustin Pop
733 68857643 Michael Hanselmann
  if callable(sha1):
734 68857643 Michael Hanselmann
    fp = sha1()
735 68857643 Michael Hanselmann
  else:
736 68857643 Michael Hanselmann
    fp = sha1.new()
737 a8083063 Iustin Pop
  while True:
738 a8083063 Iustin Pop
    data = f.read(4096)
739 a8083063 Iustin Pop
    if not data:
740 a8083063 Iustin Pop
      break
741 a8083063 Iustin Pop
742 a8083063 Iustin Pop
    fp.update(data)
743 a8083063 Iustin Pop
744 a8083063 Iustin Pop
  return fp.hexdigest()
745 a8083063 Iustin Pop
746 a8083063 Iustin Pop
747 a8083063 Iustin Pop
def FingerprintFiles(files):
748 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
749 a8083063 Iustin Pop

750 58885d79 Iustin Pop
  @type files: list
751 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
752 58885d79 Iustin Pop
  @rtype: dict
753 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
754 58885d79 Iustin Pop
      existing files
755 a8083063 Iustin Pop

756 a8083063 Iustin Pop
  """
757 a8083063 Iustin Pop
  ret = {}
758 a8083063 Iustin Pop
759 a8083063 Iustin Pop
  for filename in files:
760 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
761 a8083063 Iustin Pop
    if cksum:
762 a8083063 Iustin Pop
      ret[filename] = cksum
763 a8083063 Iustin Pop
764 a8083063 Iustin Pop
  return ret
765 a8083063 Iustin Pop
766 a8083063 Iustin Pop
767 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
768 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
769 a5728081 Guido Trotter

770 a5728081 Guido Trotter
  @type target: dict
771 a5728081 Guido Trotter
  @param target: the dict to update
772 a5728081 Guido Trotter
  @type key_types: dict
773 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
774 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
775 a5728081 Guido Trotter
  @type allowed_values: list
776 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
777 a5728081 Guido Trotter

778 a5728081 Guido Trotter
  """
779 a5728081 Guido Trotter
  if allowed_values is None:
780 a5728081 Guido Trotter
    allowed_values = []
781 a5728081 Guido Trotter
782 8b46606c Guido Trotter
  if not isinstance(target, dict):
783 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
784 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
785 8b46606c Guido Trotter
786 a5728081 Guido Trotter
  for key in target:
787 a5728081 Guido Trotter
    if key not in key_types:
788 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
789 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
790 a5728081 Guido Trotter
791 a5728081 Guido Trotter
    if target[key] in allowed_values:
792 a5728081 Guido Trotter
      continue
793 a5728081 Guido Trotter
794 29921401 Iustin Pop
    ktype = key_types[key]
795 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
796 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
797 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
798 a5728081 Guido Trotter
799 29921401 Iustin Pop
    if ktype == constants.VTYPE_STRING:
800 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
801 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
802 a5728081 Guido Trotter
          target[key] = ''
803 a5728081 Guido Trotter
        else:
804 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
805 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
806 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
807 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
808 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
809 a5728081 Guido Trotter
          target[key] = False
810 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
811 a5728081 Guido Trotter
          target[key] = True
812 a5728081 Guido Trotter
        else:
813 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
814 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
815 a5728081 Guido Trotter
      elif target[key]:
816 a5728081 Guido Trotter
        target[key] = True
817 a5728081 Guido Trotter
      else:
818 a5728081 Guido Trotter
        target[key] = False
819 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
820 a5728081 Guido Trotter
      try:
821 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
822 a5728081 Guido Trotter
      except errors.UnitParseError, err:
823 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
824 a5728081 Guido Trotter
              (key, target[key], err)
825 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
826 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
827 a5728081 Guido Trotter
      try:
828 a5728081 Guido Trotter
        target[key] = int(target[key])
829 a5728081 Guido Trotter
      except (ValueError, TypeError):
830 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
831 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
832 a5728081 Guido Trotter
833 a5728081 Guido Trotter
834 a8083063 Iustin Pop
def IsProcessAlive(pid):
835 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
836 a8083063 Iustin Pop

837 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
838 44bf25ff Iustin Pop
      will be returned as alive
839 58885d79 Iustin Pop
  @type pid: int
840 58885d79 Iustin Pop
  @param pid: the process ID to check
841 58885d79 Iustin Pop
  @rtype: boolean
842 58885d79 Iustin Pop
  @return: True if the process exists
843 a8083063 Iustin Pop

844 a8083063 Iustin Pop
  """
845 5ef5ea45 Guido Trotter
  def _TryStat(name):
846 5ef5ea45 Guido Trotter
    try:
847 5ef5ea45 Guido Trotter
      os.stat(name)
848 5ef5ea45 Guido Trotter
      return True
849 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
850 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
851 5ef5ea45 Guido Trotter
        return False
852 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
853 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
854 5ef5ea45 Guido Trotter
      raise
855 5ef5ea45 Guido Trotter
856 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
857 d9f311d7 Iustin Pop
  if pid <= 0:
858 d9f311d7 Iustin Pop
    return False
859 d9f311d7 Iustin Pop
860 5ef5ea45 Guido Trotter
  proc_entry = "/proc/%d/status" % pid
861 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
862 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
863 a8083063 Iustin Pop
  try:
864 5ef5ea45 Guido Trotter
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5, args=[proc_entry])
865 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
866 5ef5ea45 Guido Trotter
    err.RaiseInner()
867 a8083063 Iustin Pop
868 a8083063 Iustin Pop
869 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
870 58885d79 Iustin Pop
  """Read a pid from a file.
871 fee80e90 Guido Trotter

872 58885d79 Iustin Pop
  @type  pidfile: string
873 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
874 58885d79 Iustin Pop
  @rtype: int
875 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
876 d9f311d7 Iustin Pop
           otherwise 0
877 fee80e90 Guido Trotter

878 fee80e90 Guido Trotter
  """
879 fee80e90 Guido Trotter
  try:
880 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
881 d9f311d7 Iustin Pop
  except EnvironmentError, err:
882 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
883 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
884 d9f311d7 Iustin Pop
    return 0
885 fee80e90 Guido Trotter
886 fee80e90 Guido Trotter
  try:
887 13998ef2 Michael Hanselmann
    pid = int(raw_data)
888 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
889 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
890 d9f311d7 Iustin Pop
    return 0
891 fee80e90 Guido Trotter
892 d9f311d7 Iustin Pop
  return pid
893 fee80e90 Guido Trotter
894 fee80e90 Guido Trotter
895 debed9ae Michael Hanselmann
def ReadLockedPidFile(path):
896 debed9ae Michael Hanselmann
  """Reads a locked PID file.
897 debed9ae Michael Hanselmann

898 debed9ae Michael Hanselmann
  This can be used together with L{StartDaemon}.
899 debed9ae Michael Hanselmann

900 debed9ae Michael Hanselmann
  @type path: string
901 debed9ae Michael Hanselmann
  @param path: Path to PID file
902 debed9ae Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
903 debed9ae Michael Hanselmann

904 debed9ae Michael Hanselmann
  """
905 debed9ae Michael Hanselmann
  try:
906 debed9ae Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
907 debed9ae Michael Hanselmann
  except EnvironmentError, err:
908 debed9ae Michael Hanselmann
    if err.errno == errno.ENOENT:
909 debed9ae Michael Hanselmann
      # PID file doesn't exist
910 debed9ae Michael Hanselmann
      return None
911 debed9ae Michael Hanselmann
    raise
912 debed9ae Michael Hanselmann
913 debed9ae Michael Hanselmann
  try:
914 debed9ae Michael Hanselmann
    try:
915 debed9ae Michael Hanselmann
      # Try to acquire lock
916 debed9ae Michael Hanselmann
      LockFile(fd)
917 debed9ae Michael Hanselmann
    except errors.LockError:
918 debed9ae Michael Hanselmann
      # Couldn't lock, daemon is running
919 debed9ae Michael Hanselmann
      return int(os.read(fd, 100))
920 debed9ae Michael Hanselmann
  finally:
921 debed9ae Michael Hanselmann
    os.close(fd)
922 debed9ae Michael Hanselmann
923 debed9ae Michael Hanselmann
  return None
924 debed9ae Michael Hanselmann
925 debed9ae Michael Hanselmann
926 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
927 a8083063 Iustin Pop
  """Try to match a name against a list.
928 a8083063 Iustin Pop

929 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
930 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
931 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
932 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
933 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
934 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
935 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
936 a8083063 Iustin Pop

937 58885d79 Iustin Pop
  @type key: str
938 58885d79 Iustin Pop
  @param key: the name to be searched
939 58885d79 Iustin Pop
  @type name_list: list
940 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
941 256eb94b Guido Trotter
  @type case_sensitive: boolean
942 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
943 a8083063 Iustin Pop

944 58885d79 Iustin Pop
  @rtype: None or str
945 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
946 58885d79 Iustin Pop
      otherwise the element from the list which matches
947 a8083063 Iustin Pop

948 a8083063 Iustin Pop
  """
949 3a541d90 Iustin Pop
  if key in name_list:
950 3a541d90 Iustin Pop
    return key
951 256eb94b Guido Trotter
952 256eb94b Guido Trotter
  re_flags = 0
953 256eb94b Guido Trotter
  if not case_sensitive:
954 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
955 099c52ad Iustin Pop
    key = key.upper()
956 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
957 256eb94b Guido Trotter
  names_filtered = []
958 256eb94b Guido Trotter
  string_matches = []
959 256eb94b Guido Trotter
  for name in name_list:
960 256eb94b Guido Trotter
    if mo.match(name) is not None:
961 256eb94b Guido Trotter
      names_filtered.append(name)
962 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
963 256eb94b Guido Trotter
        string_matches.append(name)
964 256eb94b Guido Trotter
965 256eb94b Guido Trotter
  if len(string_matches) == 1:
966 256eb94b Guido Trotter
    return string_matches[0]
967 256eb94b Guido Trotter
  if len(names_filtered) == 1:
968 256eb94b Guido Trotter
    return names_filtered[0]
969 256eb94b Guido Trotter
  return None
970 a8083063 Iustin Pop
971 a8083063 Iustin Pop
972 bcf043c9 Iustin Pop
class HostInfo:
973 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
974 bcf043c9 Iustin Pop

975 bcf043c9 Iustin Pop
  """
976 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
977 26288e68 Iustin Pop
978 89e1fc26 Iustin Pop
  def __init__(self, name=None):
979 bcf043c9 Iustin Pop
    """Initialize the host name object.
980 bcf043c9 Iustin Pop

981 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
982 89e1fc26 Iustin Pop
    name.
983 bcf043c9 Iustin Pop

984 bcf043c9 Iustin Pop
    """
985 89e1fc26 Iustin Pop
    if name is None:
986 89e1fc26 Iustin Pop
      name = self.SysName()
987 89e1fc26 Iustin Pop
988 89e1fc26 Iustin Pop
    self.query = name
989 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
990 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
991 bcf043c9 Iustin Pop
992 c8a0948f Michael Hanselmann
  def ShortName(self):
993 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
994 c8a0948f Michael Hanselmann

995 c8a0948f Michael Hanselmann
    """
996 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
997 c8a0948f Michael Hanselmann
998 89e1fc26 Iustin Pop
  @staticmethod
999 89e1fc26 Iustin Pop
  def SysName():
1000 89e1fc26 Iustin Pop
    """Return the current system's name.
1001 bcf043c9 Iustin Pop

1002 58885d79 Iustin Pop
    This is simply a wrapper over C{socket.gethostname()}.
1003 a8083063 Iustin Pop

1004 89e1fc26 Iustin Pop
    """
1005 89e1fc26 Iustin Pop
    return socket.gethostname()
1006 a8083063 Iustin Pop
1007 89e1fc26 Iustin Pop
  @staticmethod
1008 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
1009 89e1fc26 Iustin Pop
    """Look up hostname
1010 a8083063 Iustin Pop

1011 58885d79 Iustin Pop
    @type hostname: str
1012 58885d79 Iustin Pop
    @param hostname: hostname to look up
1013 89e1fc26 Iustin Pop

1014 58885d79 Iustin Pop
    @rtype: tuple
1015 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
1016 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
1017 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
1018 89e1fc26 Iustin Pop

1019 89e1fc26 Iustin Pop
    """
1020 89e1fc26 Iustin Pop
    try:
1021 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
1022 89e1fc26 Iustin Pop
    except socket.gaierror, err:
1023 89e1fc26 Iustin Pop
      # hostname not found in DNS
1024 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
1025 a8083063 Iustin Pop
1026 89e1fc26 Iustin Pop
    return result
1027 a8083063 Iustin Pop
1028 26288e68 Iustin Pop
  @classmethod
1029 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
1030 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
1031 26288e68 Iustin Pop

1032 26288e68 Iustin Pop
    @attention: the validation is a bit more relaxed than the standards
1033 26288e68 Iustin Pop
        require; most importantly, we allow underscores in names
1034 26288e68 Iustin Pop
    @raise errors.OpPrereqError: when the name is not valid
1035 26288e68 Iustin Pop

1036 26288e68 Iustin Pop
    """
1037 26288e68 Iustin Pop
    hostname = hostname.lower()
1038 26288e68 Iustin Pop
    if (not cls._VALID_NAME_RE.match(hostname) or
1039 26288e68 Iustin Pop
        # double-dots, meaning empty label
1040 26288e68 Iustin Pop
        ".." in hostname or
1041 26288e68 Iustin Pop
        # empty initial label
1042 26288e68 Iustin Pop
        hostname.startswith(".")):
1043 26288e68 Iustin Pop
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
1044 26288e68 Iustin Pop
                                 errors.ECODE_INVAL)
1045 26288e68 Iustin Pop
    if hostname.endswith("."):
1046 26288e68 Iustin Pop
      hostname = hostname.rstrip(".")
1047 26288e68 Iustin Pop
    return hostname
1048 26288e68 Iustin Pop
1049 a8083063 Iustin Pop
1050 104f4ca1 Iustin Pop
def GetHostInfo(name=None):
1051 104f4ca1 Iustin Pop
  """Lookup host name and raise an OpPrereqError for failures"""
1052 104f4ca1 Iustin Pop
1053 104f4ca1 Iustin Pop
  try:
1054 104f4ca1 Iustin Pop
    return HostInfo(name)
1055 104f4ca1 Iustin Pop
  except errors.ResolverError, err:
1056 104f4ca1 Iustin Pop
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
1057 104f4ca1 Iustin Pop
                               (err[0], err[2]), errors.ECODE_RESOLVER)
1058 104f4ca1 Iustin Pop
1059 104f4ca1 Iustin Pop
1060 a8083063 Iustin Pop
def ListVolumeGroups():
1061 a8083063 Iustin Pop
  """List volume groups and their size
1062 a8083063 Iustin Pop

1063 58885d79 Iustin Pop
  @rtype: dict
1064 58885d79 Iustin Pop
  @return:
1065 58885d79 Iustin Pop
       Dictionary with keys volume name and values
1066 58885d79 Iustin Pop
       the size of the volume
1067 a8083063 Iustin Pop

1068 a8083063 Iustin Pop
  """
1069 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
1070 a8083063 Iustin Pop
  result = RunCmd(command)
1071 a8083063 Iustin Pop
  retval = {}
1072 a8083063 Iustin Pop
  if result.failed:
1073 a8083063 Iustin Pop
    return retval
1074 a8083063 Iustin Pop
1075 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
1076 a8083063 Iustin Pop
    try:
1077 a8083063 Iustin Pop
      name, size = line.split()
1078 a8083063 Iustin Pop
      size = int(float(size))
1079 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
1080 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
1081 a8083063 Iustin Pop
      continue
1082 a8083063 Iustin Pop
1083 a8083063 Iustin Pop
    retval[name] = size
1084 a8083063 Iustin Pop
1085 a8083063 Iustin Pop
  return retval
1086 a8083063 Iustin Pop
1087 a8083063 Iustin Pop
1088 a8083063 Iustin Pop
def BridgeExists(bridge):
1089 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
1090 a8083063 Iustin Pop

1091 58885d79 Iustin Pop
  @type bridge: str
1092 58885d79 Iustin Pop
  @param bridge: the bridge name to check
1093 58885d79 Iustin Pop
  @rtype: boolean
1094 58885d79 Iustin Pop
  @return: True if it does
1095 a8083063 Iustin Pop

1096 a8083063 Iustin Pop
  """
1097 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
1098 a8083063 Iustin Pop
1099 a8083063 Iustin Pop
1100 a8083063 Iustin Pop
def NiceSort(name_list):
1101 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
1102 a8083063 Iustin Pop

1103 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
1104 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
1105 58885d79 Iustin Pop
  'a11']}.
1106 a8083063 Iustin Pop

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

1111 58885d79 Iustin Pop
  @type name_list: list
1112 58885d79 Iustin Pop
  @param name_list: the names to be sorted
1113 58885d79 Iustin Pop
  @rtype: list
1114 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
1115 a8083063 Iustin Pop

1116 a8083063 Iustin Pop
  """
1117 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
1118 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
1119 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1120 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
1121 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
1122 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
1123 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
1124 a8083063 Iustin Pop
  def _TryInt(val):
1125 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
1126 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
1127 a8083063 Iustin Pop
      return val
1128 a8083063 Iustin Pop
    rval = int(val)
1129 a8083063 Iustin Pop
    return rval
1130 a8083063 Iustin Pop
1131 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
1132 a8083063 Iustin Pop
             for name in name_list]
1133 a8083063 Iustin Pop
  to_sort.sort()
1134 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
1135 a8083063 Iustin Pop
1136 a8083063 Iustin Pop
1137 a8083063 Iustin Pop
def TryConvert(fn, val):
1138 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
1139 a8083063 Iustin Pop

1140 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
1141 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
1142 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
1143 58885d79 Iustin Pop
  exceptions are propagated to the caller.
1144 58885d79 Iustin Pop

1145 58885d79 Iustin Pop
  @type fn: callable
1146 58885d79 Iustin Pop
  @param fn: function to apply to the value
1147 58885d79 Iustin Pop
  @param val: the value to be converted
1148 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
1149 58885d79 Iustin Pop
      otherwise the original value.
1150 a8083063 Iustin Pop

1151 a8083063 Iustin Pop
  """
1152 a8083063 Iustin Pop
  try:
1153 a8083063 Iustin Pop
    nv = fn(val)
1154 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
1155 a8083063 Iustin Pop
    nv = val
1156 a8083063 Iustin Pop
  return nv
1157 a8083063 Iustin Pop
1158 a8083063 Iustin Pop
1159 a8083063 Iustin Pop
def IsValidIP(ip):
1160 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
1161 a8083063 Iustin Pop

1162 58885d79 Iustin Pop
  This function checks if the IPv4 address passes is valid or not based
1163 58885d79 Iustin Pop
  on syntax (not IP range, class calculations, etc.).
1164 58885d79 Iustin Pop

1165 58885d79 Iustin Pop
  @type ip: str
1166 58885d79 Iustin Pop
  @param ip: the address to be checked
1167 58885d79 Iustin Pop
  @rtype: a regular expression match object
1168 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
1169 58885d79 Iustin Pop
      address is not valid
1170 a8083063 Iustin Pop

1171 a8083063 Iustin Pop
  """
1172 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
1173 58885d79 Iustin Pop
  #TODO: convert and return only boolean
1174 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
1175 a8083063 Iustin Pop
1176 a8083063 Iustin Pop
1177 a8083063 Iustin Pop
def IsValidShellParam(word):
1178 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
1179 a8083063 Iustin Pop

1180 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
1181 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
1182 a8083063 Iustin Pop
  the actual command.
1183 a8083063 Iustin Pop

1184 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
1185 a8083063 Iustin Pop
  side.
1186 a8083063 Iustin Pop

1187 58885d79 Iustin Pop
  @type word: str
1188 58885d79 Iustin Pop
  @param word: the word to check
1189 58885d79 Iustin Pop
  @rtype: boolean
1190 58885d79 Iustin Pop
  @return: True if the word is 'safe'
1191 58885d79 Iustin Pop

1192 a8083063 Iustin Pop
  """
1193 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
1194 a8083063 Iustin Pop
1195 a8083063 Iustin Pop
1196 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
1197 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
1198 a8083063 Iustin Pop

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

1204 58885d79 Iustin Pop
  @type template: str
1205 58885d79 Iustin Pop
  @param template: the string holding the template for the
1206 58885d79 Iustin Pop
      string formatting
1207 58885d79 Iustin Pop
  @rtype: str
1208 58885d79 Iustin Pop
  @return: the expanded command line
1209 58885d79 Iustin Pop

1210 a8083063 Iustin Pop
  """
1211 a8083063 Iustin Pop
  for word in args:
1212 a8083063 Iustin Pop
    if not IsValidShellParam(word):
1213 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
1214 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
1215 a8083063 Iustin Pop
  return template % args
1216 a8083063 Iustin Pop
1217 a8083063 Iustin Pop
1218 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
1219 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
1220 a8083063 Iustin Pop

1221 58885d79 Iustin Pop
  @type value: int
1222 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
1223 9fbfbb7b Iustin Pop
  @type units: char
1224 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
1225 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
1226 9fbfbb7b Iustin Pop
      - 'm' for MiBs
1227 9fbfbb7b Iustin Pop
      - 'g' for GiBs
1228 9fbfbb7b Iustin Pop
      - 't' for TiBs
1229 58885d79 Iustin Pop
  @rtype: str
1230 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
1231 a8083063 Iustin Pop

1232 a8083063 Iustin Pop
  """
1233 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
1234 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
1235 a8083063 Iustin Pop
1236 9fbfbb7b Iustin Pop
  suffix = ''
1237 9fbfbb7b Iustin Pop
1238 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
1239 9fbfbb7b Iustin Pop
    if units == 'h':
1240 9fbfbb7b Iustin Pop
      suffix = 'M'
1241 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
1242 9fbfbb7b Iustin Pop
1243 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
1244 9fbfbb7b Iustin Pop
    if units == 'h':
1245 9fbfbb7b Iustin Pop
      suffix = 'G'
1246 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
1247 a8083063 Iustin Pop
1248 a8083063 Iustin Pop
  else:
1249 9fbfbb7b Iustin Pop
    if units == 'h':
1250 9fbfbb7b Iustin Pop
      suffix = 'T'
1251 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
1252 a8083063 Iustin Pop
1253 a8083063 Iustin Pop
1254 a8083063 Iustin Pop
def ParseUnit(input_string):
1255 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
1256 a8083063 Iustin Pop

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

1261 a8083063 Iustin Pop
  """
1262 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
1263 a8083063 Iustin Pop
  if not m:
1264 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
1265 a8083063 Iustin Pop
1266 a8083063 Iustin Pop
  value = float(m.groups()[0])
1267 a8083063 Iustin Pop
1268 a8083063 Iustin Pop
  unit = m.groups()[1]
1269 a8083063 Iustin Pop
  if unit:
1270 a8083063 Iustin Pop
    lcunit = unit.lower()
1271 a8083063 Iustin Pop
  else:
1272 a8083063 Iustin Pop
    lcunit = 'm'
1273 a8083063 Iustin Pop
1274 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
1275 a8083063 Iustin Pop
    # Value already in MiB
1276 a8083063 Iustin Pop
    pass
1277 a8083063 Iustin Pop
1278 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
1279 a8083063 Iustin Pop
    value *= 1024
1280 a8083063 Iustin Pop
1281 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
1282 a8083063 Iustin Pop
    value *= 1024 * 1024
1283 a8083063 Iustin Pop
1284 a8083063 Iustin Pop
  else:
1285 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
1286 a8083063 Iustin Pop
1287 a8083063 Iustin Pop
  # Make sure we round up
1288 a8083063 Iustin Pop
  if int(value) < value:
1289 a8083063 Iustin Pop
    value += 1
1290 a8083063 Iustin Pop
1291 a8083063 Iustin Pop
  # Round up to the next multiple of 4
1292 a8083063 Iustin Pop
  value = int(value)
1293 a8083063 Iustin Pop
  if value % 4:
1294 a8083063 Iustin Pop
    value += 4 - value % 4
1295 a8083063 Iustin Pop
1296 a8083063 Iustin Pop
  return value
1297 a8083063 Iustin Pop
1298 a8083063 Iustin Pop
1299 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
1300 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1301 a8083063 Iustin Pop

1302 58885d79 Iustin Pop
  @type file_name: str
1303 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1304 58885d79 Iustin Pop
  @type key: str
1305 58885d79 Iustin Pop
  @param key: string containing key
1306 58885d79 Iustin Pop

1307 a8083063 Iustin Pop
  """
1308 a8083063 Iustin Pop
  key_fields = key.split()
1309 a8083063 Iustin Pop
1310 a8083063 Iustin Pop
  f = open(file_name, 'a+')
1311 a8083063 Iustin Pop
  try:
1312 a8083063 Iustin Pop
    nl = True
1313 a8083063 Iustin Pop
    for line in f:
1314 a8083063 Iustin Pop
      # Ignore whitespace changes
1315 a8083063 Iustin Pop
      if line.split() == key_fields:
1316 a8083063 Iustin Pop
        break
1317 a8083063 Iustin Pop
      nl = line.endswith('\n')
1318 a8083063 Iustin Pop
    else:
1319 a8083063 Iustin Pop
      if not nl:
1320 a8083063 Iustin Pop
        f.write("\n")
1321 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1322 a8083063 Iustin Pop
      f.write("\n")
1323 a8083063 Iustin Pop
      f.flush()
1324 a8083063 Iustin Pop
  finally:
1325 a8083063 Iustin Pop
    f.close()
1326 a8083063 Iustin Pop
1327 a8083063 Iustin Pop
1328 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1329 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1330 a8083063 Iustin Pop

1331 58885d79 Iustin Pop
  @type file_name: str
1332 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1333 58885d79 Iustin Pop
  @type key: str
1334 58885d79 Iustin Pop
  @param key: string containing key
1335 58885d79 Iustin Pop

1336 a8083063 Iustin Pop
  """
1337 a8083063 Iustin Pop
  key_fields = key.split()
1338 a8083063 Iustin Pop
1339 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1340 a8083063 Iustin Pop
  try:
1341 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1342 a8083063 Iustin Pop
    try:
1343 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1344 59f82e3f Michael Hanselmann
      try:
1345 59f82e3f Michael Hanselmann
        for line in f:
1346 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1347 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1348 59f82e3f Michael Hanselmann
            out.write(line)
1349 899d2a81 Michael Hanselmann
1350 899d2a81 Michael Hanselmann
        out.flush()
1351 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1352 899d2a81 Michael Hanselmann
      finally:
1353 899d2a81 Michael Hanselmann
        f.close()
1354 899d2a81 Michael Hanselmann
    finally:
1355 899d2a81 Michael Hanselmann
      out.close()
1356 899d2a81 Michael Hanselmann
  except:
1357 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1358 899d2a81 Michael Hanselmann
    raise
1359 899d2a81 Michael Hanselmann
1360 899d2a81 Michael Hanselmann
1361 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1362 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1363 899d2a81 Michael Hanselmann

1364 58885d79 Iustin Pop
  @type file_name: str
1365 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1366 58885d79 Iustin Pop
  @type ip: str
1367 58885d79 Iustin Pop
  @param ip: the IP address
1368 58885d79 Iustin Pop
  @type hostname: str
1369 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1370 58885d79 Iustin Pop
  @type aliases: list
1371 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1372 58885d79 Iustin Pop

1373 899d2a81 Michael Hanselmann
  """
1374 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1375 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1376 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1377 7fbb1f65 Michael Hanselmann
1378 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1379 899d2a81 Michael Hanselmann
  try:
1380 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1381 9440aeab Michael Hanselmann
    try:
1382 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1383 9440aeab Michael Hanselmann
      try:
1384 9440aeab Michael Hanselmann
        for line in f:
1385 9440aeab Michael Hanselmann
          fields = line.split()
1386 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1387 9440aeab Michael Hanselmann
            continue
1388 9440aeab Michael Hanselmann
          out.write(line)
1389 9440aeab Michael Hanselmann
1390 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1391 9440aeab Michael Hanselmann
        if aliases:
1392 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1393 9440aeab Michael Hanselmann
        out.write('\n')
1394 9440aeab Michael Hanselmann
1395 9440aeab Michael Hanselmann
        out.flush()
1396 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1397 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1398 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1399 9440aeab Michael Hanselmann
      finally:
1400 9440aeab Michael Hanselmann
        f.close()
1401 9440aeab Michael Hanselmann
    finally:
1402 9440aeab Michael Hanselmann
      out.close()
1403 9440aeab Michael Hanselmann
  except:
1404 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1405 9440aeab Michael Hanselmann
    raise
1406 899d2a81 Michael Hanselmann
1407 899d2a81 Michael Hanselmann
1408 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1409 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1410 d9c02ca6 Michael Hanselmann

1411 58885d79 Iustin Pop
  @type hostname: str
1412 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1413 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1414 58885d79 Iustin Pop

1415 d9c02ca6 Michael Hanselmann
  """
1416 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1417 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1418 d9c02ca6 Michael Hanselmann
1419 d9c02ca6 Michael Hanselmann
1420 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1421 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1422 899d2a81 Michael Hanselmann

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

1425 58885d79 Iustin Pop
  @type file_name: str
1426 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1427 58885d79 Iustin Pop
  @type hostname: str
1428 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1429 58885d79 Iustin Pop

1430 899d2a81 Michael Hanselmann
  """
1431 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1432 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1433 899d2a81 Michael Hanselmann
  try:
1434 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
1435 899d2a81 Michael Hanselmann
    try:
1436 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
1437 899d2a81 Michael Hanselmann
      try:
1438 899d2a81 Michael Hanselmann
        for line in f:
1439 899d2a81 Michael Hanselmann
          fields = line.split()
1440 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
1441 899d2a81 Michael Hanselmann
            names = fields[1:]
1442 899d2a81 Michael Hanselmann
            if hostname in names:
1443 899d2a81 Michael Hanselmann
              while hostname in names:
1444 899d2a81 Michael Hanselmann
                names.remove(hostname)
1445 899d2a81 Michael Hanselmann
              if names:
1446 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1447 899d2a81 Michael Hanselmann
              continue
1448 899d2a81 Michael Hanselmann
1449 899d2a81 Michael Hanselmann
          out.write(line)
1450 59f82e3f Michael Hanselmann
1451 59f82e3f Michael Hanselmann
        out.flush()
1452 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1453 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1454 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1455 59f82e3f Michael Hanselmann
      finally:
1456 59f82e3f Michael Hanselmann
        f.close()
1457 a8083063 Iustin Pop
    finally:
1458 59f82e3f Michael Hanselmann
      out.close()
1459 59f82e3f Michael Hanselmann
  except:
1460 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1461 59f82e3f Michael Hanselmann
    raise
1462 a8083063 Iustin Pop
1463 a8083063 Iustin Pop
1464 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1465 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1466 d9c02ca6 Michael Hanselmann

1467 58885d79 Iustin Pop
  @type hostname: str
1468 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1469 58885d79 Iustin Pop
      full and shot name will be removed from
1470 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1471 58885d79 Iustin Pop

1472 d9c02ca6 Michael Hanselmann
  """
1473 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1474 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1475 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1476 d9c02ca6 Michael Hanselmann
1477 d9c02ca6 Michael Hanselmann
1478 1d466a4f Michael Hanselmann
def TimestampForFilename():
1479 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1480 1d466a4f Michael Hanselmann

1481 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1482 1d466a4f Michael Hanselmann
  separators.
1483 1d466a4f Michael Hanselmann

1484 1d466a4f Michael Hanselmann
  """
1485 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1486 1d466a4f Michael Hanselmann
1487 1d466a4f Michael Hanselmann
1488 a8083063 Iustin Pop
def CreateBackup(file_name):
1489 a8083063 Iustin Pop
  """Creates a backup of a file.
1490 a8083063 Iustin Pop

1491 58885d79 Iustin Pop
  @type file_name: str
1492 58885d79 Iustin Pop
  @param file_name: file to be backed up
1493 58885d79 Iustin Pop
  @rtype: str
1494 58885d79 Iustin Pop
  @return: the path to the newly created backup
1495 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1496 a8083063 Iustin Pop

1497 a8083063 Iustin Pop
  """
1498 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1499 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1500 3ecf6786 Iustin Pop
                                file_name)
1501 a8083063 Iustin Pop
1502 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1503 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1504 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1505 081b1e69 Michael Hanselmann
1506 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1507 081b1e69 Michael Hanselmann
  try:
1508 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1509 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1510 081b1e69 Michael Hanselmann
    try:
1511 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1512 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1513 081b1e69 Michael Hanselmann
    finally:
1514 081b1e69 Michael Hanselmann
      fdst.close()
1515 081b1e69 Michael Hanselmann
  finally:
1516 081b1e69 Michael Hanselmann
    fsrc.close()
1517 081b1e69 Michael Hanselmann
1518 a8083063 Iustin Pop
  return backup_name
1519 a8083063 Iustin Pop
1520 a8083063 Iustin Pop
1521 a8083063 Iustin Pop
def ShellQuote(value):
1522 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1523 3ecf6786 Iustin Pop

1524 58885d79 Iustin Pop
  @type value: str
1525 58885d79 Iustin Pop
  @param value: the argument to be quoted
1526 58885d79 Iustin Pop
  @rtype: str
1527 58885d79 Iustin Pop
  @return: the quoted value
1528 58885d79 Iustin Pop

1529 a8083063 Iustin Pop
  """
1530 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1531 a8083063 Iustin Pop
    return value
1532 a8083063 Iustin Pop
  else:
1533 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1534 a8083063 Iustin Pop
1535 a8083063 Iustin Pop
1536 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1537 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1538 58885d79 Iustin Pop

1539 58885d79 Iustin Pop
  @type args: list
1540 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1541 58885d79 Iustin Pop
  @rtype: str
1542 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1543 a8083063 Iustin Pop

1544 a8083063 Iustin Pop
  """
1545 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1546 88d14415 Michael Hanselmann
1547 88d14415 Michael Hanselmann
1548 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1549 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1550 2c30e9d7 Alexander Schreiber

1551 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1552 58885d79 Iustin Pop
  to it.
1553 58885d79 Iustin Pop

1554 58885d79 Iustin Pop
  @type target: str
1555 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1556 58885d79 Iustin Pop
  @type port: int
1557 58885d79 Iustin Pop
  @param port: the port to connect to
1558 58885d79 Iustin Pop
  @type timeout: int
1559 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1560 58885d79 Iustin Pop
  @type live_port_needed: boolean
1561 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1562 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1563 58885d79 Iustin Pop
  @type source: str or None
1564 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1565 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1566 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1567 2c30e9d7 Alexander Schreiber

1568 2c30e9d7 Alexander Schreiber
  """
1569 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1570 2c30e9d7 Alexander Schreiber
1571 0b5ad33e Iustin Pop
  success = False
1572 2c30e9d7 Alexander Schreiber
1573 b15d625f Iustin Pop
  if source is not None:
1574 b15d625f Iustin Pop
    try:
1575 b15d625f Iustin Pop
      sock.bind((source, 0))
1576 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1577 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1578 b15d625f Iustin Pop
        success = False
1579 2c30e9d7 Alexander Schreiber
1580 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1581 2c30e9d7 Alexander Schreiber
1582 2c30e9d7 Alexander Schreiber
  try:
1583 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1584 2c30e9d7 Alexander Schreiber
    sock.close()
1585 2c30e9d7 Alexander Schreiber
    success = True
1586 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1587 2c30e9d7 Alexander Schreiber
    success = False
1588 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1589 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1590 2c30e9d7 Alexander Schreiber
1591 2c30e9d7 Alexander Schreiber
  return success
1592 eedbda4b Michael Hanselmann
1593 eedbda4b Michael Hanselmann
1594 caad16e2 Iustin Pop
def OwnIpAddress(address):
1595 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1596 caad16e2 Iustin Pop

1597 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1598 caad16e2 Iustin Pop
  address.
1599 caad16e2 Iustin Pop

1600 caad16e2 Iustin Pop
  @type address: string
1601 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1602 caad16e2 Iustin Pop
  @rtype: bool
1603 58885d79 Iustin Pop
  @return: True if we own the address
1604 caad16e2 Iustin Pop

1605 caad16e2 Iustin Pop
  """
1606 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1607 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1608 caad16e2 Iustin Pop
1609 caad16e2 Iustin Pop
1610 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1611 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1612 58885d79 Iustin Pop

1613 58885d79 Iustin Pop
  @type path: str
1614 58885d79 Iustin Pop
  @param path: the directory to enumerate
1615 58885d79 Iustin Pop
  @rtype: list
1616 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1617 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1618 eedbda4b Michael Hanselmann

1619 eedbda4b Michael Hanselmann
  """
1620 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1621 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1622 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1623 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1624 f3299a07 Michael Hanselmann
  files.sort()
1625 f3299a07 Michael Hanselmann
  return files
1626 2f8b60b3 Iustin Pop
1627 2f8b60b3 Iustin Pop
1628 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1629 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1630 257f4c0a Iustin Pop

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

1635 2f8b60b3 Iustin Pop
  """
1636 2f8b60b3 Iustin Pop
  try:
1637 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1638 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1639 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1640 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1641 257f4c0a Iustin Pop
    else:
1642 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1643 257f4c0a Iustin Pop
                                   type(user))
1644 2f8b60b3 Iustin Pop
  except KeyError:
1645 2f8b60b3 Iustin Pop
    return default
1646 2f8b60b3 Iustin Pop
  return result.pw_dir
1647 59072e7e Michael Hanselmann
1648 59072e7e Michael Hanselmann
1649 24818e8f Michael Hanselmann
def NewUUID():
1650 59072e7e Michael Hanselmann
  """Returns a random UUID.
1651 59072e7e Michael Hanselmann

1652 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1653 58885d79 Iustin Pop
      filesystem.
1654 58885d79 Iustin Pop
  @rtype: str
1655 58885d79 Iustin Pop

1656 59072e7e Michael Hanselmann
  """
1657 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1658 087b34fe Iustin Pop
1659 087b34fe Iustin Pop
1660 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1661 33081d90 Iustin Pop
  """Generates a random secret.
1662 33081d90 Iustin Pop

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

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

1671 33081d90 Iustin Pop
  """
1672 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1673 33081d90 Iustin Pop
1674 33081d90 Iustin Pop
1675 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1676 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1677 9dae41ad Guido Trotter

1678 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1679 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1680 9dae41ad Guido Trotter

1681 9dae41ad Guido Trotter
  """
1682 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1683 9dae41ad Guido Trotter
    try:
1684 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1685 9dae41ad Guido Trotter
    except EnvironmentError, err:
1686 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1687 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1688 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1689 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1690 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1691 9dae41ad Guido Trotter
1692 9dae41ad Guido Trotter
1693 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1694 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1695 ca0aa6d0 Michael Hanselmann

1696 016308cb Iustin Pop
  @type size: int
1697 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1698 58885d79 Iustin Pop
  @rtype: str
1699 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1700 ca0aa6d0 Michael Hanselmann

1701 ca0aa6d0 Michael Hanselmann
  """
1702 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1703 ca0aa6d0 Michael Hanselmann
  try:
1704 582ed043 Guido Trotter
    return f.read(size)
1705 ca0aa6d0 Michael Hanselmann
  finally:
1706 ca0aa6d0 Michael Hanselmann
    f.close()
1707 ca0aa6d0 Michael Hanselmann
1708 ca0aa6d0 Michael Hanselmann
1709 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1710 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1711 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1712 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1713 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1714 087b34fe Iustin Pop
  """(Over)write a file atomically.
1715 087b34fe Iustin Pop

1716 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1717 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1718 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1719 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1720 087b34fe Iustin Pop
  mtime/atime of the file.
1721 087b34fe Iustin Pop

1722 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1723 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1724 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1725 087b34fe Iustin Pop
  temporary file should be removed.
1726 087b34fe Iustin Pop

1727 58885d79 Iustin Pop
  @type file_name: str
1728 58885d79 Iustin Pop
  @param file_name: the target filename
1729 58885d79 Iustin Pop
  @type fn: callable
1730 58885d79 Iustin Pop
  @param fn: content writing function, called with
1731 58885d79 Iustin Pop
      file descriptor as parameter
1732 69efe319 Michael Hanselmann
  @type data: str
1733 58885d79 Iustin Pop
  @param data: contents of the file
1734 58885d79 Iustin Pop
  @type mode: int
1735 58885d79 Iustin Pop
  @param mode: file mode
1736 58885d79 Iustin Pop
  @type uid: int
1737 58885d79 Iustin Pop
  @param uid: the owner of the file
1738 58885d79 Iustin Pop
  @type gid: int
1739 58885d79 Iustin Pop
  @param gid: the group of the file
1740 58885d79 Iustin Pop
  @type atime: int
1741 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1742 58885d79 Iustin Pop
  @type mtime: int
1743 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1744 58885d79 Iustin Pop
  @type close: boolean
1745 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1746 58885d79 Iustin Pop
  @type prewrite: callable
1747 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1748 58885d79 Iustin Pop
  @type postwrite: callable
1749 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1750 58885d79 Iustin Pop

1751 58885d79 Iustin Pop
  @rtype: None or int
1752 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1753 58885d79 Iustin Pop
      otherwise the file descriptor
1754 58885d79 Iustin Pop

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

1757 087b34fe Iustin Pop
  """
1758 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1759 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1760 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1761 087b34fe Iustin Pop
1762 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1763 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1764 087b34fe Iustin Pop
1765 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1766 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1767 087b34fe Iustin Pop
                                 " set or None")
1768 087b34fe Iustin Pop
1769 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1770 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1771 087b34fe Iustin Pop
1772 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1773 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1774 81b7354c Iustin Pop
  do_remove = True
1775 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1776 087b34fe Iustin Pop
  # leaves it in place
1777 087b34fe Iustin Pop
  try:
1778 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1779 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1780 087b34fe Iustin Pop
    if mode:
1781 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1782 71714516 Michael Hanselmann
    if callable(prewrite):
1783 71714516 Michael Hanselmann
      prewrite(fd)
1784 087b34fe Iustin Pop
    if data is not None:
1785 087b34fe Iustin Pop
      os.write(fd, data)
1786 087b34fe Iustin Pop
    else:
1787 087b34fe Iustin Pop
      fn(fd)
1788 71714516 Michael Hanselmann
    if callable(postwrite):
1789 71714516 Michael Hanselmann
      postwrite(fd)
1790 087b34fe Iustin Pop
    os.fsync(fd)
1791 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1792 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1793 70f4497c Michael Hanselmann
    if not dry_run:
1794 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1795 81b7354c Iustin Pop
      do_remove = False
1796 087b34fe Iustin Pop
  finally:
1797 71714516 Michael Hanselmann
    if close:
1798 71714516 Michael Hanselmann
      os.close(fd)
1799 71714516 Michael Hanselmann
      result = None
1800 71714516 Michael Hanselmann
    else:
1801 71714516 Michael Hanselmann
      result = fd
1802 81b7354c Iustin Pop
    if do_remove:
1803 81b7354c Iustin Pop
      RemoveFile(new_name)
1804 78feb6fb Guido Trotter
1805 71714516 Michael Hanselmann
  return result
1806 71714516 Michael Hanselmann
1807 78feb6fb Guido Trotter
1808 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
1809 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
1810 e587b46a Guido Trotter

1811 e587b46a Guido Trotter
  @type strict: boolean
1812 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1813 e587b46a Guido Trotter
      non-empty line
1814 e587b46a Guido Trotter

1815 e587b46a Guido Trotter
  """
1816 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1817 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1818 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1819 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1820 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1821 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1822 e587b46a Guido Trotter
                              file_name)
1823 e587b46a Guido Trotter
  return full_lines[0]
1824 e587b46a Guido Trotter
1825 e587b46a Guido Trotter
1826 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1827 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1828 7b4126b7 Iustin Pop

1829 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1830 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1831 7b4126b7 Iustin Pop
  value, the index will be returned.
1832 7b4126b7 Iustin Pop

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

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

1838 58885d79 Iustin Pop
  @type seq: sequence
1839 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1840 58885d79 Iustin Pop
  @type base: int
1841 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1842 58885d79 Iustin Pop
  @rtype: int
1843 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1844 7b4126b7 Iustin Pop

1845 7b4126b7 Iustin Pop
  """
1846 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1847 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1848 7b4126b7 Iustin Pop
    if elem > idx + base:
1849 7b4126b7 Iustin Pop
      # idx is not used
1850 7b4126b7 Iustin Pop
      return idx + base
1851 7b4126b7 Iustin Pop
  return None
1852 7b4126b7 Iustin Pop
1853 7b4126b7 Iustin Pop
1854 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1855 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1856 dcd511c8 Guido Trotter

1857 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1858 dfdc4060 Guido Trotter

1859 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1860 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1861 dfdc4060 Guido Trotter
  @type event: integer
1862 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1863 dcd511c8 Guido Trotter
  @type timeout: float or None
1864 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1865 dcd511c8 Guido Trotter
  @rtype: int or None
1866 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1867 dcd511c8 Guido Trotter

1868 dcd511c8 Guido Trotter
  """
1869 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1870 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1871 dcd511c8 Guido Trotter
1872 dcd511c8 Guido Trotter
  if timeout is not None:
1873 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1874 dcd511c8 Guido Trotter
    timeout *= 1000
1875 dcd511c8 Guido Trotter
1876 dcd511c8 Guido Trotter
  poller = select.poll()
1877 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1878 dcd511c8 Guido Trotter
  try:
1879 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1880 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1881 dfdc4060 Guido Trotter
    # every so often.
1882 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1883 dfdc4060 Guido Trotter
  except select.error, err:
1884 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1885 dfdc4060 Guido Trotter
      raise
1886 dfdc4060 Guido Trotter
    io_events = []
1887 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1888 dfdc4060 Guido Trotter
    return io_events[0][1]
1889 dfdc4060 Guido Trotter
  else:
1890 dfdc4060 Guido Trotter
    return None
1891 dfdc4060 Guido Trotter
1892 dfdc4060 Guido Trotter
1893 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1894 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1895 dfdc4060 Guido Trotter

1896 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1897 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1898 dfdc4060 Guido Trotter
  expired.
1899 dfdc4060 Guido Trotter

1900 dfdc4060 Guido Trotter
  """
1901 dfdc4060 Guido Trotter
1902 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1903 dfdc4060 Guido Trotter
    self.timeout = timeout
1904 dfdc4060 Guido Trotter
1905 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1906 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1907 dfdc4060 Guido Trotter
    if result is None:
1908 dfdc4060 Guido Trotter
      raise RetryAgain()
1909 dfdc4060 Guido Trotter
    else:
1910 dfdc4060 Guido Trotter
      return result
1911 dfdc4060 Guido Trotter
1912 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1913 dfdc4060 Guido Trotter
    self.timeout = timeout
1914 dfdc4060 Guido Trotter
1915 dfdc4060 Guido Trotter
1916 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1917 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1918 dfdc4060 Guido Trotter

1919 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1920 dfdc4060 Guido Trotter

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

1930 dfdc4060 Guido Trotter
  """
1931 dfdc4060 Guido Trotter
  if timeout is not None:
1932 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1933 1b429e2a Iustin Pop
    try:
1934 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1935 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1936 1b429e2a Iustin Pop
    except RetryTimeout:
1937 1b429e2a Iustin Pop
      result = None
1938 dfdc4060 Guido Trotter
  else:
1939 dfdc4060 Guido Trotter
    result = None
1940 dfdc4060 Guido Trotter
    while result is None:
1941 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1942 dfdc4060 Guido Trotter
  return result
1943 2de64672 Iustin Pop
1944 2de64672 Iustin Pop
1945 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1946 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1947 f7414041 Michael Hanselmann

1948 f7414041 Michael Hanselmann
  Element order is preserved.
1949 58885d79 Iustin Pop

1950 58885d79 Iustin Pop
  @type seq: sequence
1951 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1952 58885d79 Iustin Pop
  @rtype: list
1953 58885d79 Iustin Pop
  @return: list of unique elements from seq
1954 58885d79 Iustin Pop

1955 f7414041 Michael Hanselmann
  """
1956 f7414041 Michael Hanselmann
  seen = set()
1957 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1958 1862d460 Alexander Schreiber
1959 1862d460 Alexander Schreiber
1960 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
1961 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
1962 1862d460 Alexander Schreiber

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

1966 58885d79 Iustin Pop
  @type mac: str
1967 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1968 82187135 René Nussbaumer
  @rtype: str
1969 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
1970 82187135 René Nussbaumer

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

1973 1862d460 Alexander Schreiber
  """
1974 82187135 René Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1975 82187135 René Nussbaumer
  if not mac_check.match(mac):
1976 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1977 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
1978 82187135 René Nussbaumer
1979 82187135 René Nussbaumer
  return mac.lower()
1980 06009e27 Iustin Pop
1981 06009e27 Iustin Pop
1982 06009e27 Iustin Pop
def TestDelay(duration):
1983 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1984 06009e27 Iustin Pop

1985 58885d79 Iustin Pop
  @type duration: float
1986 58885d79 Iustin Pop
  @param duration: the sleep duration
1987 58885d79 Iustin Pop
  @rtype: boolean
1988 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1989 58885d79 Iustin Pop

1990 06009e27 Iustin Pop
  """
1991 06009e27 Iustin Pop
  if duration < 0:
1992 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1993 06009e27 Iustin Pop
  time.sleep(duration)
1994 38ea42a1 Iustin Pop
  return True, None
1995 8f765069 Iustin Pop
1996 8f765069 Iustin Pop
1997 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1998 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1999 8f765069 Iustin Pop

2000 7d88772a Iustin Pop
  @type fd: int
2001 7d88772a Iustin Pop
  @param fd: the file descriptor
2002 7d88772a Iustin Pop
  @type retries: int
2003 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
2004 7d88772a Iustin Pop
      other error than EBADF
2005 7d88772a Iustin Pop

2006 7d88772a Iustin Pop
  """
2007 7d88772a Iustin Pop
  try:
2008 7d88772a Iustin Pop
    os.close(fd)
2009 7d88772a Iustin Pop
  except OSError, err:
2010 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
2011 7d88772a Iustin Pop
      if retries > 0:
2012 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
2013 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
2014 7d88772a Iustin Pop
    # ignore this and go on
2015 7d88772a Iustin Pop
2016 7d88772a Iustin Pop
2017 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
2018 7d88772a Iustin Pop
  """Close file descriptors.
2019 7d88772a Iustin Pop

2020 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
2021 7d88772a Iustin Pop
  stdin/out/err).
2022 8f765069 Iustin Pop

2023 58885d79 Iustin Pop
  @type noclose_fds: list or None
2024 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
2025 58885d79 Iustin Pop
      that should not be closed
2026 58885d79 Iustin Pop

2027 8f765069 Iustin Pop
  """
2028 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
2029 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
2030 8f765069 Iustin Pop
    try:
2031 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
2032 8f765069 Iustin Pop
      if MAXFD < 0:
2033 8f765069 Iustin Pop
        MAXFD = 1024
2034 8f765069 Iustin Pop
    except OSError:
2035 8f765069 Iustin Pop
      MAXFD = 1024
2036 8f765069 Iustin Pop
  else:
2037 8f765069 Iustin Pop
    MAXFD = 1024
2038 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
2039 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
2040 7d88772a Iustin Pop
    maxfd = MAXFD
2041 7d88772a Iustin Pop
2042 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
2043 7d88772a Iustin Pop
  for fd in range(3, maxfd):
2044 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
2045 7d88772a Iustin Pop
      continue
2046 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2047 7d88772a Iustin Pop
2048 7d88772a Iustin Pop
2049 4b6fa0bf Luca Bigliardi
def Mlockall():
2050 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
2051 4b6fa0bf Luca Bigliardi

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

2055 4b6fa0bf Luca Bigliardi
  """
2056 4b6fa0bf Luca Bigliardi
  if ctypes is None:
2057 4b6fa0bf Luca Bigliardi
    logging.warning("Cannot set memory lock, ctypes module not found")
2058 4b6fa0bf Luca Bigliardi
    return
2059 4b6fa0bf Luca Bigliardi
2060 4b6fa0bf Luca Bigliardi
  libc = ctypes.cdll.LoadLibrary("libc.so.6")
2061 4b6fa0bf Luca Bigliardi
  if libc is None:
2062 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
2063 4b6fa0bf Luca Bigliardi
    return
2064 4b6fa0bf Luca Bigliardi
2065 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
2066 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
2067 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
2068 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
2069 4b6fa0bf Luca Bigliardi
  # the actual error code was.
2070 20601361 Luca Bigliardi
  # pylint: disable-msg=W0212
2071 4b6fa0bf Luca Bigliardi
  libc.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
2072 4b6fa0bf Luca Bigliardi
2073 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
2074 20601361 Luca Bigliardi
    # pylint: disable-msg=W0212
2075 6ed0bbce Luca Bigliardi
    logging.error("Cannot set memory lock: %s",
2076 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
2077 4b6fa0bf Luca Bigliardi
    return
2078 4b6fa0bf Luca Bigliardi
2079 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
2080 4b6fa0bf Luca Bigliardi
2081 4b6fa0bf Luca Bigliardi
2082 7d88772a Iustin Pop
def Daemonize(logfile):
2083 7d88772a Iustin Pop
  """Daemonize the current process.
2084 7d88772a Iustin Pop

2085 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
2086 7d88772a Iustin Pop
  runs it in the background as a daemon.
2087 7d88772a Iustin Pop

2088 7d88772a Iustin Pop
  @type logfile: str
2089 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
2090 7d88772a Iustin Pop
  @rtype: int
2091 5fcc718f Iustin Pop
  @return: the value zero
2092 7d88772a Iustin Pop

2093 7d88772a Iustin Pop
  """
2094 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
2095 7260cfbe Iustin Pop
  # yes, we really want os._exit
2096 7d88772a Iustin Pop
  UMASK = 077
2097 7d88772a Iustin Pop
  WORKDIR = "/"
2098 8f765069 Iustin Pop
2099 8f765069 Iustin Pop
  # this might fail
2100 8f765069 Iustin Pop
  pid = os.fork()
2101 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
2102 8f765069 Iustin Pop
    os.setsid()
2103 8f765069 Iustin Pop
    # this might fail
2104 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
2105 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
2106 8f765069 Iustin Pop
      os.chdir(WORKDIR)
2107 8f765069 Iustin Pop
      os.umask(UMASK)
2108 8f765069 Iustin Pop
    else:
2109 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
2110 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
2111 8f765069 Iustin Pop
  else:
2112 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
2113 8f765069 Iustin Pop
2114 7d88772a Iustin Pop
  for fd in range(3):
2115 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
2116 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
2117 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
2118 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
2119 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
2120 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
2121 7d88772a Iustin Pop
  os.dup2(1, 2)
2122 8f765069 Iustin Pop
  return 0
2123 57c177af Iustin Pop
2124 57c177af Iustin Pop
2125 53beffbb Iustin Pop
def DaemonPidFileName(name):
2126 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
2127 58885d79 Iustin Pop

2128 58885d79 Iustin Pop
  @type name: str
2129 58885d79 Iustin Pop
  @param name: the daemon name
2130 58885d79 Iustin Pop
  @rtype: str
2131 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
2132 58885d79 Iustin Pop
      daemon name
2133 b330ac0b Guido Trotter

2134 b330ac0b Guido Trotter
  """
2135 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
2136 b330ac0b Guido Trotter
2137 b330ac0b Guido Trotter
2138 2826b361 Guido Trotter
def EnsureDaemon(name):
2139 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
2140 2826b361 Guido Trotter

2141 2826b361 Guido Trotter
  """
2142 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
2143 2826b361 Guido Trotter
  if result.failed:
2144 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
2145 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
2146 2826b361 Guido Trotter
    return False
2147 2826b361 Guido Trotter
2148 2826b361 Guido Trotter
  return True
2149 b330ac0b Guido Trotter
2150 b330ac0b Guido Trotter
2151 b330ac0b Guido Trotter
def WritePidFile(name):
2152 b330ac0b Guido Trotter
  """Write the current process pidfile.
2153 b330ac0b Guido Trotter

2154 58885d79 Iustin Pop
  The file will be written to L{constants.RUN_GANETI_DIR}I{/name.pid}
2155 58885d79 Iustin Pop

2156 58885d79 Iustin Pop
  @type name: str
2157 58885d79 Iustin Pop
  @param name: the daemon name to use
2158 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
2159 58885d79 Iustin Pop
      points to a live process
2160 b330ac0b Guido Trotter

2161 b330ac0b Guido Trotter
  """
2162 b330ac0b Guido Trotter
  pid = os.getpid()
2163 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2164 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
2165 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
2166 b330ac0b Guido Trotter
2167 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
2168 b330ac0b Guido Trotter
2169 b330ac0b Guido Trotter
2170 b330ac0b Guido Trotter
def RemovePidFile(name):
2171 b330ac0b Guido Trotter
  """Remove the current process pidfile.
2172 b330ac0b Guido Trotter

2173 b330ac0b Guido Trotter
  Any errors are ignored.
2174 b330ac0b Guido Trotter

2175 58885d79 Iustin Pop
  @type name: str
2176 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
2177 58885d79 Iustin Pop

2178 b330ac0b Guido Trotter
  """
2179 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
2180 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
2181 b330ac0b Guido Trotter
  try:
2182 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
2183 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
2184 b330ac0b Guido Trotter
    pass
2185 b330ac0b Guido Trotter
2186 b330ac0b Guido Trotter
2187 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
2188 ff5251bc Iustin Pop
                waitpid=False):
2189 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
2190 b2a1f511 Iustin Pop

2191 b2a1f511 Iustin Pop
  @type pid: int
2192 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
2193 38206f3c Iustin Pop
  @type signal_: int
2194 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
2195 b2a1f511 Iustin Pop
  @type timeout: int
2196 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
2197 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
2198 b2a1f511 Iustin Pop
                  will be done
2199 ff5251bc Iustin Pop
  @type waitpid: boolean
2200 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
2201 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
2202 ff5251bc Iustin Pop
      would remain as zombie
2203 b2a1f511 Iustin Pop

2204 b2a1f511 Iustin Pop
  """
2205 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
2206 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
2207 ff5251bc Iustin Pop
    os.kill(pid, signal_)
2208 ff5251bc Iustin Pop
    if wait:
2209 ff5251bc Iustin Pop
      try:
2210 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
2211 ff5251bc Iustin Pop
      except OSError:
2212 ff5251bc Iustin Pop
        pass
2213 ff5251bc Iustin Pop
2214 b2a1f511 Iustin Pop
  if pid <= 0:
2215 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
2216 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
2217 b2a1f511 Iustin Pop
2218 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
2219 b2a1f511 Iustin Pop
    return
2220 31892b4c Michael Hanselmann
2221 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
2222 31892b4c Michael Hanselmann
2223 b2a1f511 Iustin Pop
  if timeout <= 0:
2224 b2a1f511 Iustin Pop
    return
2225 7167159a Michael Hanselmann
2226 31892b4c Michael Hanselmann
  def _CheckProcess():
2227 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
2228 31892b4c Michael Hanselmann
      return
2229 31892b4c Michael Hanselmann
2230 7167159a Michael Hanselmann
    try:
2231 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
2232 7167159a Michael Hanselmann
    except OSError:
2233 31892b4c Michael Hanselmann
      raise RetryAgain()
2234 31892b4c Michael Hanselmann
2235 31892b4c Michael Hanselmann
    if result_pid > 0:
2236 31892b4c Michael Hanselmann
      return
2237 31892b4c Michael Hanselmann
2238 31892b4c Michael Hanselmann
    raise RetryAgain()
2239 31892b4c Michael Hanselmann
2240 31892b4c Michael Hanselmann
  try:
2241 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
2242 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
2243 31892b4c Michael Hanselmann
  except RetryTimeout:
2244 31892b4c Michael Hanselmann
    pass
2245 7167159a Michael Hanselmann
2246 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
2247 7167159a Michael Hanselmann
    # Kill process if it's still alive
2248 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
2249 b2a1f511 Iustin Pop
2250 b2a1f511 Iustin Pop
2251 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
2252 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
2253 57c177af Iustin Pop

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

2257 58885d79 Iustin Pop
  @type name: str
2258 58885d79 Iustin Pop
  @param name: the name to look for
2259 58885d79 Iustin Pop
  @type search_path: str
2260 58885d79 Iustin Pop
  @param search_path: location to start at
2261 58885d79 Iustin Pop
  @type test: callable
2262 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
2263 58885d79 Iustin Pop
      if the a given object is valid; the default value is
2264 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
2265 58885d79 Iustin Pop
  @rtype: str or None
2266 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
2267 57c177af Iustin Pop

2268 57c177af Iustin Pop
  """
2269 f95c81bf Iustin Pop
  # validate the filename mask
2270 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
2271 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
2272 f95c81bf Iustin Pop
                     name)
2273 f95c81bf Iustin Pop
    return None
2274 f95c81bf Iustin Pop
2275 57c177af Iustin Pop
  for dir_name in search_path:
2276 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
2277 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
2278 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
2279 f95c81bf Iustin Pop
    # basename
2280 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
2281 57c177af Iustin Pop
      return item_name
2282 57c177af Iustin Pop
  return None
2283 8d1a2a64 Michael Hanselmann
2284 8d1a2a64 Michael Hanselmann
2285 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
2286 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
2287 8d1a2a64 Michael Hanselmann

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

2291 58885d79 Iustin Pop
  @type vglist: dict
2292 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2293 58885d79 Iustin Pop
  @type vgname: str
2294 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2295 58885d79 Iustin Pop
  @type minsize: int
2296 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2297 58885d79 Iustin Pop
  @rtype: None or str
2298 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2299 8d1a2a64 Michael Hanselmann

2300 8d1a2a64 Michael Hanselmann
  """
2301 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2302 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2303 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2304 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2305 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2306 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2307 8d1a2a64 Michael Hanselmann
  return None
2308 7996a135 Iustin Pop
2309 7996a135 Iustin Pop
2310 45bc5e4a Michael Hanselmann
def SplitTime(value):
2311 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2312 739be818 Michael Hanselmann

2313 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2314 45bc5e4a Michael Hanselmann
  @type value: int or float
2315 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2316 739be818 Michael Hanselmann

2317 739be818 Michael Hanselmann
  """
2318 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2319 45bc5e4a Michael Hanselmann
2320 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2321 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2322 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2323 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2324 45bc5e4a Michael Hanselmann
2325 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2326 739be818 Michael Hanselmann
2327 739be818 Michael Hanselmann
2328 739be818 Michael Hanselmann
def MergeTime(timetuple):
2329 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2330 739be818 Michael Hanselmann

2331 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2332 739be818 Michael Hanselmann
  @type timetuple: tuple
2333 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2334 739be818 Michael Hanselmann

2335 739be818 Michael Hanselmann
  """
2336 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2337 739be818 Michael Hanselmann
2338 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2339 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2340 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2341 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2342 739be818 Michael Hanselmann
2343 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2344 739be818 Michael Hanselmann
2345 739be818 Michael Hanselmann
2346 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
2347 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
2348 4a8b186a Michael Hanselmann

2349 4a8b186a Michael Hanselmann
  Note that this routine does not read a ganeti-specific file, but
2350 58885d79 Iustin Pop
  instead uses C{socket.getservbyname} to allow pre-customization of
2351 4a8b186a Michael Hanselmann
  this parameter outside of Ganeti.
2352 4a8b186a Michael Hanselmann

2353 cd50653c Guido Trotter
  @type daemon_name: string
2354 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2355 58885d79 Iustin Pop
  @rtype: int
2356 58885d79 Iustin Pop

2357 4a8b186a Michael Hanselmann
  """
2358 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
2359 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
2360 cd50653c Guido Trotter
2361 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
2362 4a8b186a Michael Hanselmann
  try:
2363 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
2364 4a8b186a Michael Hanselmann
  except socket.error:
2365 cd50653c Guido Trotter
    port = default_port
2366 4a8b186a Michael Hanselmann
2367 4a8b186a Michael Hanselmann
  return port
2368 4a8b186a Michael Hanselmann
2369 4a8b186a Michael Hanselmann
2370 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2371 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2372 de3b8e39 Luca Bigliardi

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

2377 de3b8e39 Luca Bigliardi
  """
2378 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2379 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2380 de3b8e39 Luca Bigliardi

2381 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2382 de3b8e39 Luca Bigliardi

2383 de3b8e39 Luca Bigliardi
    """
2384 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2385 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2386 de3b8e39 Luca Bigliardi
2387 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2388 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2389 de3b8e39 Luca Bigliardi

2390 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2391 de3b8e39 Luca Bigliardi
    /dev/console.
2392 de3b8e39 Luca Bigliardi

2393 de3b8e39 Luca Bigliardi
    """
2394 de3b8e39 Luca Bigliardi
    try:
2395 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2396 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2397 de3b8e39 Luca Bigliardi
      try:
2398 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2399 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2400 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2401 de3b8e39 Luca Bigliardi
        pass
2402 de3b8e39 Luca Bigliardi
2403 de3b8e39 Luca Bigliardi
2404 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2405 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2406 49e60a28 Luca Bigliardi
                 console_logging=False):
2407 82d9caef Iustin Pop
  """Configures the logging module.
2408 82d9caef Iustin Pop

2409 58885d79 Iustin Pop
  @type logfile: str
2410 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2411 ea34193f Iustin Pop
  @type debug: integer
2412 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2413 58885d79 Iustin Pop
      only those at C{INFO} and above level
2414 58885d79 Iustin Pop
  @type stderr_logging: boolean
2415 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2416 58885d79 Iustin Pop
  @type program: str
2417 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2418 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2419 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2420 551b6283 Iustin Pop
  @type syslog: string
2421 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2422 551b6283 Iustin Pop
      - if no, syslog is not used
2423 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2424 551b6283 Iustin Pop
      - if only, only syslog is used
2425 49e60a28 Luca Bigliardi
  @type console_logging: boolean
2426 49e60a28 Luca Bigliardi
  @param console_logging: if True, will use a FileHandler which falls back to
2427 49e60a28 Luca Bigliardi
      the system console if logging fails
2428 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2429 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2430 58885d79 Iustin Pop

2431 82d9caef Iustin Pop
  """
2432 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2433 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2434 d21d09d6 Iustin Pop
  if multithreaded:
2435 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2436 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2437 82d9caef Iustin Pop
  if debug:
2438 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2439 551b6283 Iustin Pop
    # no debug info for syslog loggers
2440 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2441 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2442 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2443 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2444 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2445 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2446 82d9caef Iustin Pop
2447 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2448 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2449 82d9caef Iustin Pop
2450 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2451 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2452 7d88772a Iustin Pop
    handler.close()
2453 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2454 6346a9e5 Michael Hanselmann
2455 82d9caef Iustin Pop
  if stderr_logging:
2456 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2457 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2458 82d9caef Iustin Pop
    if debug:
2459 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2460 82d9caef Iustin Pop
    else:
2461 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2462 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2463 82d9caef Iustin Pop
2464 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2465 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2466 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2467 551b6283 Iustin Pop
                                                    facility)
2468 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2469 551b6283 Iustin Pop
    # Never enable debug over syslog
2470 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2471 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2472 551b6283 Iustin Pop
2473 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2474 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2475 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2476 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2477 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2478 551b6283 Iustin Pop
    try:
2479 49e60a28 Luca Bigliardi
      if console_logging:
2480 49e60a28 Luca Bigliardi
        logfile_handler = LogFileHandler(logfile)
2481 49e60a28 Luca Bigliardi
      else:
2482 49e60a28 Luca Bigliardi
        logfile_handler = logging.FileHandler(logfile)
2483 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2484 551b6283 Iustin Pop
      if debug:
2485 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2486 551b6283 Iustin Pop
      else:
2487 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2488 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2489 551b6283 Iustin Pop
    except EnvironmentError:
2490 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2491 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2492 551b6283 Iustin Pop
      else:
2493 551b6283 Iustin Pop
        # we need to re-raise the exception
2494 551b6283 Iustin Pop
        raise
2495 82d9caef Iustin Pop
2496 016d04b3 Michael Hanselmann
2497 da961187 Guido Trotter
def IsNormAbsPath(path):
2498 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2499 da961187 Guido Trotter

2500 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2501 da961187 Guido Trotter

2502 da961187 Guido Trotter
  """
2503 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2504 82d9caef Iustin Pop
2505 016d04b3 Michael Hanselmann
2506 4bb678e9 Iustin Pop
def PathJoin(*args):
2507 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2508 4bb678e9 Iustin Pop

2509 4bb678e9 Iustin Pop
  Requirements:
2510 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2511 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2512 4bb678e9 Iustin Pop
        since we check for normalization at the end
2513 4bb678e9 Iustin Pop

2514 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2515 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2516 4bb678e9 Iustin Pop

2517 4bb678e9 Iustin Pop
  """
2518 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2519 4bb678e9 Iustin Pop
  assert args
2520 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2521 4bb678e9 Iustin Pop
  root = args[0]
2522 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2523 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2524 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2525 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2526 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2527 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2528 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2529 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2530 4bb678e9 Iustin Pop
  if prefix != root:
2531 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2532 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2533 4bb678e9 Iustin Pop
  return result
2534 4bb678e9 Iustin Pop
2535 4bb678e9 Iustin Pop
2536 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2537 f65f63ef Iustin Pop
  """Return the last lines from a file.
2538 f65f63ef Iustin Pop

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

2543 f65f63ef Iustin Pop
  @param fname: the file name
2544 f65f63ef Iustin Pop
  @type lines: int
2545 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2546 f65f63ef Iustin Pop

2547 f65f63ef Iustin Pop
  """
2548 f65f63ef Iustin Pop
  fd = open(fname, "r")
2549 f65f63ef Iustin Pop
  try:
2550 f65f63ef Iustin Pop
    fd.seek(0, 2)
2551 f65f63ef Iustin Pop
    pos = fd.tell()
2552 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2553 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2554 f65f63ef Iustin Pop
    raw_data = fd.read()
2555 f65f63ef Iustin Pop
  finally:
2556 f65f63ef Iustin Pop
    fd.close()
2557 f65f63ef Iustin Pop
2558 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2559 f65f63ef Iustin Pop
  return rows[-lines:]
2560 f65f63ef Iustin Pop
2561 f65f63ef Iustin Pop
2562 24d70417 Michael Hanselmann
def FormatTimestampWithTZ(secs):
2563 24d70417 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
2564 24d70417 Michael Hanselmann

2565 24d70417 Michael Hanselmann
  """
2566 24d70417 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
2567 24d70417 Michael Hanselmann
2568 24d70417 Michael Hanselmann
2569 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2570 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2571 27e46076 Michael Hanselmann

2572 27e46076 Michael Hanselmann
  @type value: string
2573 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2574 27e46076 Michael Hanselmann

2575 27e46076 Michael Hanselmann
  """
2576 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2577 27e46076 Michael Hanselmann
  if m:
2578 27e46076 Michael Hanselmann
    # We have an offset
2579 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2580 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2581 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2582 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2583 27e46076 Michael Hanselmann
  else:
2584 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2585 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2586 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2587 27e46076 Michael Hanselmann
    utcoffset = 0
2588 27e46076 Michael Hanselmann
2589 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2590 27e46076 Michael Hanselmann
2591 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2592 27e46076 Michael Hanselmann
2593 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2594 27e46076 Michael Hanselmann
2595 27e46076 Michael Hanselmann
2596 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2597 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2598 27e46076 Michael Hanselmann

2599 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2600 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2601 27e46076 Michael Hanselmann

2602 27e46076 Michael Hanselmann
  """
2603 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2604 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2605 27e46076 Michael Hanselmann
  try:
2606 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2607 27e46076 Michael Hanselmann
  except AttributeError:
2608 27e46076 Michael Hanselmann
    not_before = None
2609 27e46076 Michael Hanselmann
  else:
2610 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2611 27e46076 Michael Hanselmann
2612 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2613 27e46076 Michael Hanselmann
      not_before = None
2614 27e46076 Michael Hanselmann
    else:
2615 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2616 27e46076 Michael Hanselmann
2617 27e46076 Michael Hanselmann
  try:
2618 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2619 27e46076 Michael Hanselmann
  except AttributeError:
2620 27e46076 Michael Hanselmann
    not_after = None
2621 27e46076 Michael Hanselmann
  else:
2622 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2623 27e46076 Michael Hanselmann
2624 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2625 27e46076 Michael Hanselmann
      not_after = None
2626 27e46076 Michael Hanselmann
    else:
2627 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2628 27e46076 Michael Hanselmann
2629 27e46076 Michael Hanselmann
  return (not_before, not_after)
2630 27e46076 Michael Hanselmann
2631 27e46076 Michael Hanselmann
2632 24d70417 Michael Hanselmann
def _VerifyCertificateInner(expired, not_before, not_after, now,
2633 24d70417 Michael Hanselmann
                            warn_days, error_days):
2634 24d70417 Michael Hanselmann
  """Verifies certificate validity.
2635 24d70417 Michael Hanselmann

2636 24d70417 Michael Hanselmann
  @type expired: bool
2637 24d70417 Michael Hanselmann
  @param expired: Whether pyOpenSSL considers the certificate as expired
2638 24d70417 Michael Hanselmann
  @type not_before: number or None
2639 24d70417 Michael Hanselmann
  @param not_before: Unix timestamp before which certificate is not valid
2640 24d70417 Michael Hanselmann
  @type not_after: number or None
2641 24d70417 Michael Hanselmann
  @param not_after: Unix timestamp after which certificate is invalid
2642 24d70417 Michael Hanselmann
  @type now: number
2643 24d70417 Michael Hanselmann
  @param now: Current time as Unix timestamp
2644 24d70417 Michael Hanselmann
  @type warn_days: number or None
2645 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2646 24d70417 Michael Hanselmann
  @type error_days: number or None
2647 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2648 24d70417 Michael Hanselmann

2649 24d70417 Michael Hanselmann
  """
2650 24d70417 Michael Hanselmann
  if expired:
2651 24d70417 Michael Hanselmann
    msg = "Certificate is expired"
2652 24d70417 Michael Hanselmann
2653 24d70417 Michael Hanselmann
    if not_before is not None and not_after is not None:
2654 24d70417 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
2655 24d70417 Michael Hanselmann
              (FormatTimestampWithTZ(not_before),
2656 24d70417 Michael Hanselmann
               FormatTimestampWithTZ(not_after)))
2657 24d70417 Michael Hanselmann
    elif not_before is not None:
2658 24d70417 Michael Hanselmann
      msg += " (valid from %s)" % FormatTimestampWithTZ(not_before)
2659 24d70417 Michael Hanselmann
    elif not_after is not None:
2660 24d70417 Michael Hanselmann
      msg += " (valid until %s)" % FormatTimestampWithTZ(not_after)
2661 24d70417 Michael Hanselmann
2662 24d70417 Michael Hanselmann
    return (CERT_ERROR, msg)
2663 24d70417 Michael Hanselmann
2664 24d70417 Michael Hanselmann
  elif not_before is not None and not_before > now:
2665 24d70417 Michael Hanselmann
    return (CERT_WARNING,
2666 24d70417 Michael Hanselmann
            "Certificate not yet valid (valid from %s)" %
2667 24d70417 Michael Hanselmann
            FormatTimestampWithTZ(not_before))
2668 24d70417 Michael Hanselmann
2669 24d70417 Michael Hanselmann
  elif not_after is not None:
2670 24d70417 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
2671 24d70417 Michael Hanselmann
2672 24d70417 Michael Hanselmann
    msg = "Certificate expires in about %d days" % remaining_days
2673 24d70417 Michael Hanselmann
2674 24d70417 Michael Hanselmann
    if error_days is not None and remaining_days <= error_days:
2675 24d70417 Michael Hanselmann
      return (CERT_ERROR, msg)
2676 24d70417 Michael Hanselmann
2677 24d70417 Michael Hanselmann
    if warn_days is not None and remaining_days <= warn_days:
2678 24d70417 Michael Hanselmann
      return (CERT_WARNING, msg)
2679 24d70417 Michael Hanselmann
2680 24d70417 Michael Hanselmann
  return (None, None)
2681 24d70417 Michael Hanselmann
2682 24d70417 Michael Hanselmann
2683 24d70417 Michael Hanselmann
def VerifyX509Certificate(cert, warn_days, error_days):
2684 24d70417 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
2685 24d70417 Michael Hanselmann

2686 24d70417 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2687 24d70417 Michael Hanselmann
  @param cert: X509 certificate object
2688 24d70417 Michael Hanselmann
  @type warn_days: number or None
2689 24d70417 Michael Hanselmann
  @param warn_days: How many days before expiration a warning should be reported
2690 24d70417 Michael Hanselmann
  @type error_days: number or None
2691 24d70417 Michael Hanselmann
  @param error_days: How many days before expiration an error should be reported
2692 24d70417 Michael Hanselmann

2693 24d70417 Michael Hanselmann
  """
2694 24d70417 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
2695 24d70417 Michael Hanselmann
  (not_before, not_after) = GetX509CertValidity(cert)
2696 24d70417 Michael Hanselmann
2697 24d70417 Michael Hanselmann
  return _VerifyCertificateInner(cert.has_expired(), not_before, not_after,
2698 24d70417 Michael Hanselmann
                                 time.time(), warn_days, error_days)
2699 24d70417 Michael Hanselmann
2700 24d70417 Michael Hanselmann
2701 68857643 Michael Hanselmann
def SignX509Certificate(cert, key, salt):
2702 68857643 Michael Hanselmann
  """Sign a X509 certificate.
2703 68857643 Michael Hanselmann

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

2706 68857643 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2707 68857643 Michael Hanselmann
  @param cert: X509 certificate object
2708 68857643 Michael Hanselmann
  @type key: string
2709 68857643 Michael Hanselmann
  @param key: Key for HMAC
2710 68857643 Michael Hanselmann
  @type salt: string
2711 68857643 Michael Hanselmann
  @param salt: Salt for HMAC
2712 68857643 Michael Hanselmann
  @rtype: string
2713 68857643 Michael Hanselmann
  @return: Serialized and signed certificate in PEM format
2714 68857643 Michael Hanselmann

2715 68857643 Michael Hanselmann
  """
2716 68857643 Michael Hanselmann
  if not VALID_X509_SIGNATURE_SALT.match(salt):
2717 68857643 Michael Hanselmann
    raise errors.GenericError("Invalid salt: %r" % salt)
2718 68857643 Michael Hanselmann
2719 68857643 Michael Hanselmann
  # Dumping as PEM here ensures the certificate is in a sane format
2720 68857643 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2721 68857643 Michael Hanselmann
2722 68857643 Michael Hanselmann
  return ("%s: %s/%s\n\n%s" %
2723 68857643 Michael Hanselmann
          (constants.X509_CERT_SIGNATURE_HEADER, salt,
2724 3718bf6d Michael Hanselmann
           Sha1Hmac(key, cert_pem, salt=salt),
2725 68857643 Michael Hanselmann
           cert_pem))
2726 68857643 Michael Hanselmann
2727 68857643 Michael Hanselmann
2728 68857643 Michael Hanselmann
def _ExtractX509CertificateSignature(cert_pem):
2729 68857643 Michael Hanselmann
  """Helper function to extract signature from X509 certificate.
2730 68857643 Michael Hanselmann

2731 68857643 Michael Hanselmann
  """
2732 68857643 Michael Hanselmann
  # Extract signature from original PEM data
2733 68857643 Michael Hanselmann
  for line in cert_pem.splitlines():
2734 68857643 Michael Hanselmann
    if line.startswith("---"):
2735 68857643 Michael Hanselmann
      break
2736 68857643 Michael Hanselmann
2737 68857643 Michael Hanselmann
    m = X509_SIGNATURE.match(line.strip())
2738 68857643 Michael Hanselmann
    if m:
2739 68857643 Michael Hanselmann
      return (m.group("salt"), m.group("sign"))
2740 68857643 Michael Hanselmann
2741 68857643 Michael Hanselmann
  raise errors.GenericError("X509 certificate signature is missing")
2742 68857643 Michael Hanselmann
2743 68857643 Michael Hanselmann
2744 68857643 Michael Hanselmann
def LoadSignedX509Certificate(cert_pem, key):
2745 68857643 Michael Hanselmann
  """Verifies a signed X509 certificate.
2746 68857643 Michael Hanselmann

2747 68857643 Michael Hanselmann
  @type cert_pem: string
2748 68857643 Michael Hanselmann
  @param cert_pem: Certificate in PEM format and with signature header
2749 68857643 Michael Hanselmann
  @type key: string
2750 68857643 Michael Hanselmann
  @param key: Key for HMAC
2751 68857643 Michael Hanselmann
  @rtype: tuple; (OpenSSL.crypto.X509, string)
2752 68857643 Michael Hanselmann
  @return: X509 certificate object and salt
2753 68857643 Michael Hanselmann

2754 68857643 Michael Hanselmann
  """
2755 68857643 Michael Hanselmann
  (salt, signature) = _ExtractX509CertificateSignature(cert_pem)
2756 68857643 Michael Hanselmann
2757 68857643 Michael Hanselmann
  # Load certificate
2758 68857643 Michael Hanselmann
  cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
2759 68857643 Michael Hanselmann
2760 68857643 Michael Hanselmann
  # Dump again to ensure it's in a sane format
2761 68857643 Michael Hanselmann
  sane_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
2762 68857643 Michael Hanselmann
2763 3718bf6d Michael Hanselmann
  if not VerifySha1Hmac(key, sane_pem, signature, salt=salt):
2764 68857643 Michael Hanselmann
    raise errors.GenericError("X509 certificate signature is invalid")
2765 68857643 Michael Hanselmann
2766 68857643 Michael Hanselmann
  return (cert, salt)
2767 68857643 Michael Hanselmann
2768 68857643 Michael Hanselmann
2769 3718bf6d Michael Hanselmann
def Sha1Hmac(key, text, salt=None):
2770 615aaaba Michael Hanselmann
  """Calculates the HMAC-SHA1 digest of a text.
2771 615aaaba Michael Hanselmann

2772 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2773 615aaaba Michael Hanselmann

2774 615aaaba Michael Hanselmann
  @type key: string
2775 615aaaba Michael Hanselmann
  @param key: Secret key
2776 615aaaba Michael Hanselmann
  @type text: string
2777 615aaaba Michael Hanselmann

2778 615aaaba Michael Hanselmann
  """
2779 3718bf6d Michael Hanselmann
  if salt:
2780 3718bf6d Michael Hanselmann
    salted_text = salt + text
2781 3718bf6d Michael Hanselmann
  else:
2782 3718bf6d Michael Hanselmann
    salted_text = text
2783 3718bf6d Michael Hanselmann
2784 3718bf6d Michael Hanselmann
  return hmac.new(key, salted_text, sha1).hexdigest()
2785 615aaaba Michael Hanselmann
2786 615aaaba Michael Hanselmann
2787 3718bf6d Michael Hanselmann
def VerifySha1Hmac(key, text, digest, salt=None):
2788 615aaaba Michael Hanselmann
  """Verifies the HMAC-SHA1 digest of a text.
2789 615aaaba Michael Hanselmann

2790 615aaaba Michael Hanselmann
  HMAC is defined in RFC2104.
2791 615aaaba Michael Hanselmann

2792 615aaaba Michael Hanselmann
  @type key: string
2793 615aaaba Michael Hanselmann
  @param key: Secret key
2794 615aaaba Michael Hanselmann
  @type text: string
2795 615aaaba Michael Hanselmann
  @type digest: string
2796 615aaaba Michael Hanselmann
  @param digest: Expected digest
2797 615aaaba Michael Hanselmann
  @rtype: bool
2798 615aaaba Michael Hanselmann
  @return: Whether HMAC-SHA1 digest matches
2799 615aaaba Michael Hanselmann

2800 615aaaba Michael Hanselmann
  """
2801 3718bf6d Michael Hanselmann
  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
2802 615aaaba Michael Hanselmann
2803 615aaaba Michael Hanselmann
2804 26f15862 Iustin Pop
def SafeEncode(text):
2805 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2806 26f15862 Iustin Pop

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

2816 26f15862 Iustin Pop
  @type text: str or unicode
2817 26f15862 Iustin Pop
  @param text: input data
2818 26f15862 Iustin Pop
  @rtype: str
2819 26f15862 Iustin Pop
  @return: a safe version of text
2820 26f15862 Iustin Pop

2821 26f15862 Iustin Pop
  """
2822 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2823 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2824 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2825 d392fa34 Iustin Pop
  resu = ""
2826 d392fa34 Iustin Pop
  for char in text:
2827 d392fa34 Iustin Pop
    c = ord(char)
2828 d392fa34 Iustin Pop
    if char  == '\t':
2829 d392fa34 Iustin Pop
      resu += r'\t'
2830 d392fa34 Iustin Pop
    elif char == '\n':
2831 d392fa34 Iustin Pop
      resu += r'\n'
2832 d392fa34 Iustin Pop
    elif char == '\r':
2833 d392fa34 Iustin Pop
      resu += r'\'r'
2834 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2835 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2836 d392fa34 Iustin Pop
    else:
2837 d392fa34 Iustin Pop
      resu += char
2838 d392fa34 Iustin Pop
  return resu
2839 26f15862 Iustin Pop
2840 26f15862 Iustin Pop
2841 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2842 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2843 5b69bc7c Iustin Pop

2844 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2845 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2846 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2847 5b69bc7c Iustin Pop
  separator):
2848 5b69bc7c Iustin Pop
    - a plain , separates the elements
2849 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2850 5b69bc7c Iustin Pop
      backslash plus a separator comma
2851 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2852 5b69bc7c Iustin Pop
      non-separator comma
2853 5b69bc7c Iustin Pop

2854 5b69bc7c Iustin Pop
  @type text: string
2855 5b69bc7c Iustin Pop
  @param text: the string to split
2856 5b69bc7c Iustin Pop
  @type sep: string
2857 5b69bc7c Iustin Pop
  @param text: the separator
2858 5b69bc7c Iustin Pop
  @rtype: string
2859 5b69bc7c Iustin Pop
  @return: a list of strings
2860 5b69bc7c Iustin Pop

2861 5b69bc7c Iustin Pop
  """
2862 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2863 5b69bc7c Iustin Pop
  slist = text.split(sep)
2864 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2865 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2866 5b69bc7c Iustin Pop
  rlist = []
2867 5b69bc7c Iustin Pop
  while slist:
2868 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2869 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2870 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2871 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2872 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2873 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2874 5b69bc7c Iustin Pop
        # the next step
2875 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2876 5b69bc7c Iustin Pop
        continue
2877 5b69bc7c Iustin Pop
    rlist.append(e1)
2878 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2879 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2880 5b69bc7c Iustin Pop
  return rlist
2881 5b69bc7c Iustin Pop
2882 5b69bc7c Iustin Pop
2883 ab3e6da8 Iustin Pop
def CommaJoin(names):
2884 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2885 ab3e6da8 Iustin Pop

2886 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2887 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2888 ab3e6da8 Iustin Pop

2889 ab3e6da8 Iustin Pop
  """
2890 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2891 ab3e6da8 Iustin Pop
2892 ab3e6da8 Iustin Pop
2893 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2894 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2895 3f6a47a8 Michael Hanselmann

2896 3f6a47a8 Michael Hanselmann
  @type value: int
2897 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2898 3f6a47a8 Michael Hanselmann
  @rtype: int
2899 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2900 3f6a47a8 Michael Hanselmann

2901 3f6a47a8 Michael Hanselmann
  """
2902 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2903 3f6a47a8 Michael Hanselmann
2904 3f6a47a8 Michael Hanselmann
2905 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2906 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2907 3f6a47a8 Michael Hanselmann

2908 3f6a47a8 Michael Hanselmann
  @type path: string
2909 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2910 3f6a47a8 Michael Hanselmann
  @rtype: int
2911 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2912 3f6a47a8 Michael Hanselmann

2913 3f6a47a8 Michael Hanselmann
  """
2914 3f6a47a8 Michael Hanselmann
  size = 0
2915 3f6a47a8 Michael Hanselmann
2916 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2917 2a887df9 Michael Hanselmann
    for filename in files:
2918 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2919 3f6a47a8 Michael Hanselmann
      size += st.st_size
2920 3f6a47a8 Michael Hanselmann
2921 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2922 3f6a47a8 Michael Hanselmann
2923 3f6a47a8 Michael Hanselmann
2924 620a85fd Iustin Pop
def GetFilesystemStats(path):
2925 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2926 3f6a47a8 Michael Hanselmann

2927 3f6a47a8 Michael Hanselmann
  @type path: string
2928 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2929 3f6a47a8 Michael Hanselmann
  @rtype: int
2930 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2931 3f6a47a8 Michael Hanselmann

2932 3f6a47a8 Michael Hanselmann
  """
2933 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2934 3f6a47a8 Michael Hanselmann
2935 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2936 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2937 620a85fd Iustin Pop
  return (tsize, fsize)
2938 3f6a47a8 Michael Hanselmann
2939 3f6a47a8 Michael Hanselmann
2940 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2941 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2942 eb58f7bd Michael Hanselmann

2943 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2944 eb58f7bd Michael Hanselmann

2945 eb58f7bd Michael Hanselmann
  @type fn: callable
2946 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2947 bdefe5dd Michael Hanselmann
  @rtype: bool
2948 bdefe5dd Michael Hanselmann
  @return: Function's result
2949 eb58f7bd Michael Hanselmann

2950 eb58f7bd Michael Hanselmann
  """
2951 eb58f7bd Michael Hanselmann
  pid = os.fork()
2952 eb58f7bd Michael Hanselmann
  if pid == 0:
2953 eb58f7bd Michael Hanselmann
    # Child process
2954 eb58f7bd Michael Hanselmann
    try:
2955 82869978 Michael Hanselmann
      # In case the function uses temporary files
2956 82869978 Michael Hanselmann
      ResetTempfileModule()
2957 82869978 Michael Hanselmann
2958 eb58f7bd Michael Hanselmann
      # Call function
2959 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2960 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2961 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2962 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2963 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2964 eb58f7bd Michael Hanselmann
      result = 33
2965 eb58f7bd Michael Hanselmann
2966 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2967 eb58f7bd Michael Hanselmann
2968 eb58f7bd Michael Hanselmann
  # Parent process
2969 eb58f7bd Michael Hanselmann
2970 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2971 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2972 eb58f7bd Michael Hanselmann
2973 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2974 eb58f7bd Michael Hanselmann
    exitcode = None
2975 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2976 eb58f7bd Michael Hanselmann
  else:
2977 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2978 eb58f7bd Michael Hanselmann
    signum = None
2979 eb58f7bd Michael Hanselmann
2980 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2981 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2982 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2983 eb58f7bd Michael Hanselmann
2984 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2985 eb58f7bd Michael Hanselmann
2986 eb58f7bd Michael Hanselmann
2987 7996a135 Iustin Pop
def LockedMethod(fn):
2988 7996a135 Iustin Pop
  """Synchronized object access decorator.
2989 7996a135 Iustin Pop

2990 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2991 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2992 7996a135 Iustin Pop

2993 7996a135 Iustin Pop
  """
2994 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2995 e67bd559 Michael Hanselmann
    if debug_locks:
2996 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2997 e67bd559 Michael Hanselmann
2998 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2999 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
3000 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
3001 7996a135 Iustin Pop
    lock = self._lock
3002 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
3003 7996a135 Iustin Pop
    lock.acquire()
3004 7996a135 Iustin Pop
    try:
3005 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
3006 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
3007 7996a135 Iustin Pop
    finally:
3008 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
3009 7996a135 Iustin Pop
      lock.release()
3010 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
3011 7996a135 Iustin Pop
    return result
3012 7996a135 Iustin Pop
  return wrapper
3013 eb0f0ce0 Michael Hanselmann
3014 eb0f0ce0 Michael Hanselmann
3015 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
3016 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
3017 eb0f0ce0 Michael Hanselmann

3018 58885d79 Iustin Pop
  @type fd: int
3019 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
3020 58885d79 Iustin Pop

3021 eb0f0ce0 Michael Hanselmann
  """
3022 eb0f0ce0 Michael Hanselmann
  try:
3023 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
3024 eb0f0ce0 Michael Hanselmann
  except IOError, err:
3025 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
3026 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
3027 eb0f0ce0 Michael Hanselmann
    raise
3028 de499029 Michael Hanselmann
3029 de499029 Michael Hanselmann
3030 3b813dd2 Iustin Pop
def FormatTime(val):
3031 3b813dd2 Iustin Pop
  """Formats a time value.
3032 3b813dd2 Iustin Pop

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

3037 3b813dd2 Iustin Pop
  """
3038 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
3039 3b813dd2 Iustin Pop
    return "N/A"
3040 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
3041 3b813dd2 Iustin Pop
  # platforms
3042 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
3043 3b813dd2 Iustin Pop
3044 3b813dd2 Iustin Pop
3045 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
3046 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
3047 05e50653 Michael Hanselmann

3048 5cbe43a5 Michael Hanselmann
  @type filename: string
3049 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
3050 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
3051 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
3052 5cbe43a5 Michael Hanselmann
  @type remove_after: int
3053 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
3054 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
3055 5cbe43a5 Michael Hanselmann

3056 05e50653 Michael Hanselmann
  """
3057 05e50653 Michael Hanselmann
  if now is None:
3058 05e50653 Michael Hanselmann
    now = time.time()
3059 05e50653 Michael Hanselmann
3060 05e50653 Michael Hanselmann
  try:
3061 05e50653 Michael Hanselmann
    value = ReadFile(filename)
3062 05e50653 Michael Hanselmann
  except IOError, err:
3063 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
3064 05e50653 Michael Hanselmann
      raise
3065 05e50653 Michael Hanselmann
    value = None
3066 05e50653 Michael Hanselmann
3067 05e50653 Michael Hanselmann
  if value is not None:
3068 05e50653 Michael Hanselmann
    try:
3069 05e50653 Michael Hanselmann
      value = int(value)
3070 05e50653 Michael Hanselmann
    except ValueError:
3071 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
3072 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
3073 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
3074 05e50653 Michael Hanselmann
      value = None
3075 05e50653 Michael Hanselmann
3076 05e50653 Michael Hanselmann
    if value is not None:
3077 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
3078 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
3079 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
3080 5cbe43a5 Michael Hanselmann
        value = None
3081 5cbe43a5 Michael Hanselmann
3082 5cbe43a5 Michael Hanselmann
      elif now > value:
3083 05e50653 Michael Hanselmann
        value = None
3084 05e50653 Michael Hanselmann
3085 05e50653 Michael Hanselmann
  return value
3086 05e50653 Michael Hanselmann
3087 05e50653 Michael Hanselmann
3088 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
3089 de0ea66b Michael Hanselmann
  """Retry loop timed out.
3090 de0ea66b Michael Hanselmann

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

3095 de0ea66b Michael Hanselmann
  """
3096 506be7c5 Guido Trotter
  def RaiseInner(self):
3097 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
3098 506be7c5 Guido Trotter
      raise self.args[0]
3099 506be7c5 Guido Trotter
    else:
3100 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
3101 de0ea66b Michael Hanselmann
3102 de0ea66b Michael Hanselmann
3103 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
3104 de0ea66b Michael Hanselmann
  """Retry again.
3105 de0ea66b Michael Hanselmann

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

3110 de0ea66b Michael Hanselmann
  """
3111 de0ea66b Michael Hanselmann
3112 de0ea66b Michael Hanselmann
3113 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
3114 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
3115 de0ea66b Michael Hanselmann

3116 de0ea66b Michael Hanselmann
  """
3117 de0ea66b Michael Hanselmann
  __slots__ = [
3118 de0ea66b Michael Hanselmann
    "_factor",
3119 de0ea66b Michael Hanselmann
    "_limit",
3120 de0ea66b Michael Hanselmann
    "_next",
3121 de0ea66b Michael Hanselmann
    "_start",
3122 de0ea66b Michael Hanselmann
    ]
3123 de0ea66b Michael Hanselmann
3124 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
3125 de0ea66b Michael Hanselmann
    """Initializes this class.
3126 de0ea66b Michael Hanselmann

3127 de0ea66b Michael Hanselmann
    @type start: float
3128 de0ea66b Michael Hanselmann
    @param start: Initial delay
3129 de0ea66b Michael Hanselmann
    @type factor: float
3130 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
3131 de0ea66b Michael Hanselmann
    @type limit: float or None
3132 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
3133 de0ea66b Michael Hanselmann

3134 de0ea66b Michael Hanselmann
    """
3135 de0ea66b Michael Hanselmann
    assert start > 0.0
3136 de0ea66b Michael Hanselmann
    assert factor >= 1.0
3137 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
3138 de0ea66b Michael Hanselmann
3139 de0ea66b Michael Hanselmann
    self._start = start
3140 de0ea66b Michael Hanselmann
    self._factor = factor
3141 de0ea66b Michael Hanselmann
    self._limit = limit
3142 de0ea66b Michael Hanselmann
3143 de0ea66b Michael Hanselmann
    self._next = start
3144 de0ea66b Michael Hanselmann
3145 de0ea66b Michael Hanselmann
  def __call__(self):
3146 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
3147 de0ea66b Michael Hanselmann

3148 de0ea66b Michael Hanselmann
    """
3149 de0ea66b Michael Hanselmann
    current = self._next
3150 de0ea66b Michael Hanselmann
3151 de0ea66b Michael Hanselmann
    # Update for next run
3152 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
3153 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
3154 de0ea66b Michael Hanselmann
3155 de0ea66b Michael Hanselmann
    return current
3156 de0ea66b Michael Hanselmann
3157 de0ea66b Michael Hanselmann
3158 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
3159 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
3160 de0ea66b Michael Hanselmann
3161 de0ea66b Michael Hanselmann
3162 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
3163 de0ea66b Michael Hanselmann
          _time_fn=time.time):
3164 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
3165 de0ea66b Michael Hanselmann

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

3170 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
3171 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
3172 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
3173 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
3174 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
3175 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
3176 de0ea66b Michael Hanselmann

3177 de0ea66b Michael Hanselmann
  @type fn: callable
3178 de0ea66b Michael Hanselmann
  @param fn: Function to be called
3179 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
3180 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
3181 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
3182 de0ea66b Michael Hanselmann
  @type timeout: float
3183 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
3184 de0ea66b Michael Hanselmann
  @type wait_fn: callable
3185 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
3186 de0ea66b Michael Hanselmann
  @return: Return value of function
3187 de0ea66b Michael Hanselmann

3188 de0ea66b Michael Hanselmann
  """
3189 de0ea66b Michael Hanselmann
  assert callable(fn)
3190 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
3191 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
3192 de0ea66b Michael Hanselmann
3193 de0ea66b Michael Hanselmann
  if args is None:
3194 de0ea66b Michael Hanselmann
    args = []
3195 de0ea66b Michael Hanselmann
3196 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
3197 de0ea66b Michael Hanselmann
3198 de0ea66b Michael Hanselmann
  if callable(delay):
3199 de0ea66b Michael Hanselmann
    # External function to calculate delay
3200 de0ea66b Michael Hanselmann
    calc_delay = delay
3201 de0ea66b Michael Hanselmann
3202 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
3203 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
3204 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
3205 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
3206 de0ea66b Michael Hanselmann
3207 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
3208 de0ea66b Michael Hanselmann
    # Always use the remaining time
3209 de0ea66b Michael Hanselmann
    calc_delay = None
3210 de0ea66b Michael Hanselmann
3211 de0ea66b Michael Hanselmann
  else:
3212 de0ea66b Michael Hanselmann
    # Static delay
3213 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
3214 de0ea66b Michael Hanselmann
3215 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
3216 de0ea66b Michael Hanselmann
3217 de0ea66b Michael Hanselmann
  while True:
3218 506be7c5 Guido Trotter
    retry_args = []
3219 de0ea66b Michael Hanselmann
    try:
3220 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
3221 de0ea66b Michael Hanselmann
      return fn(*args)
3222 506be7c5 Guido Trotter
    except RetryAgain, err:
3223 506be7c5 Guido Trotter
      retry_args = err.args
3224 1b429e2a Iustin Pop
    except RetryTimeout:
3225 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
3226 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
3227 de0ea66b Michael Hanselmann
3228 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
3229 de0ea66b Michael Hanselmann
3230 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
3231 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
3232 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
3233 de0ea66b Michael Hanselmann
3234 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
3235 de0ea66b Michael Hanselmann
3236 de0ea66b Michael Hanselmann
    if calc_delay is None:
3237 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
3238 de0ea66b Michael Hanselmann
    else:
3239 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
3240 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
3241 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
3242 de0ea66b Michael Hanselmann
3243 de0ea66b Michael Hanselmann
3244 bdd5e420 Michael Hanselmann
def GetClosedTempfile(*args, **kwargs):
3245 bdd5e420 Michael Hanselmann
  """Creates a temporary file and returns its path.
3246 a55474c7 Michael Hanselmann

3247 bdd5e420 Michael Hanselmann
  """
3248 bdd5e420 Michael Hanselmann
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
3249 bdd5e420 Michael Hanselmann
  _CloseFDNoErr(fd)
3250 bdd5e420 Michael Hanselmann
  return path
3251 bdd5e420 Michael Hanselmann
3252 bdd5e420 Michael Hanselmann
3253 bdd5e420 Michael Hanselmann
def GenerateSelfSignedX509Cert(common_name, validity):
3254 bdd5e420 Michael Hanselmann
  """Generates a self-signed X509 certificate.
3255 bdd5e420 Michael Hanselmann

3256 bdd5e420 Michael Hanselmann
  @type common_name: string
3257 bdd5e420 Michael Hanselmann
  @param common_name: commonName value
3258 a55474c7 Michael Hanselmann
  @type validity: int
3259 bdd5e420 Michael Hanselmann
  @param validity: Validity for certificate in seconds
3260 a55474c7 Michael Hanselmann

3261 a55474c7 Michael Hanselmann
  """
3262 bdd5e420 Michael Hanselmann
  # Create private and public key
3263 bdd5e420 Michael Hanselmann
  key = OpenSSL.crypto.PKey()
3264 bdd5e420 Michael Hanselmann
  key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS)
3265 bdd5e420 Michael Hanselmann
3266 bdd5e420 Michael Hanselmann
  # Create self-signed certificate
3267 bdd5e420 Michael Hanselmann
  cert = OpenSSL.crypto.X509()
3268 bdd5e420 Michael Hanselmann
  if common_name:
3269 bdd5e420 Michael Hanselmann
    cert.get_subject().CN = common_name
3270 bdd5e420 Michael Hanselmann
  cert.set_serial_number(1)
3271 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notBefore(0)
3272 bdd5e420 Michael Hanselmann
  cert.gmtime_adj_notAfter(validity)
3273 bdd5e420 Michael Hanselmann
  cert.set_issuer(cert.get_subject())
3274 bdd5e420 Michael Hanselmann
  cert.set_pubkey(key)
3275 bdd5e420 Michael Hanselmann
  cert.sign(key, constants.X509_CERT_SIGN_DIGEST)
3276 bdd5e420 Michael Hanselmann
3277 bdd5e420 Michael Hanselmann
  key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
3278 bdd5e420 Michael Hanselmann
  cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
3279 bdd5e420 Michael Hanselmann
3280 bdd5e420 Michael Hanselmann
  return (key_pem, cert_pem)
3281 bdd5e420 Michael Hanselmann
3282 bdd5e420 Michael Hanselmann
3283 bdd5e420 Michael Hanselmann
def GenerateSelfSignedSslCert(filename, validity=(5 * 365)):
3284 bdd5e420 Michael Hanselmann
  """Legacy function to generate self-signed X509 certificate.
3285 bdd5e420 Michael Hanselmann

3286 bdd5e420 Michael Hanselmann
  """
3287 bdd5e420 Michael Hanselmann
  (key_pem, cert_pem) = GenerateSelfSignedX509Cert(None,
3288 bdd5e420 Michael Hanselmann
                                                   validity * 24 * 60 * 60)
3289 bdd5e420 Michael Hanselmann
3290 bdd5e420 Michael Hanselmann
  WriteFile(filename, mode=0400, data=key_pem + cert_pem)
3291 a55474c7 Michael Hanselmann
3292 a55474c7 Michael Hanselmann
3293 a87b4824 Michael Hanselmann
class FileLock(object):
3294 a87b4824 Michael Hanselmann
  """Utility class for file locks.
3295 a87b4824 Michael Hanselmann

3296 a87b4824 Michael Hanselmann
  """
3297 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
3298 58885d79 Iustin Pop
    """Constructor for FileLock.
3299 58885d79 Iustin Pop

3300 b4478d34 Michael Hanselmann
    @type fd: file
3301 b4478d34 Michael Hanselmann
    @param fd: File object
3302 58885d79 Iustin Pop
    @type filename: str
3303 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
3304 58885d79 Iustin Pop

3305 58885d79 Iustin Pop
    """
3306 b4478d34 Michael Hanselmann
    self.fd = fd
3307 a87b4824 Michael Hanselmann
    self.filename = filename
3308 b4478d34 Michael Hanselmann
3309 b4478d34 Michael Hanselmann
  @classmethod
3310 b4478d34 Michael Hanselmann
  def Open(cls, filename):
3311 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
3312 b4478d34 Michael Hanselmann

3313 b4478d34 Michael Hanselmann
    @type filename: string
3314 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
3315 b4478d34 Michael Hanselmann

3316 b4478d34 Michael Hanselmann
    """
3317 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
3318 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
3319 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
3320 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
3321 b4478d34 Michael Hanselmann
               filename)
3322 a87b4824 Michael Hanselmann
3323 a87b4824 Michael Hanselmann
  def __del__(self):
3324 a87b4824 Michael Hanselmann
    self.Close()
3325 a87b4824 Michael Hanselmann
3326 a87b4824 Michael Hanselmann
  def Close(self):
3327 58885d79 Iustin Pop
    """Close the file and release the lock.
3328 58885d79 Iustin Pop

3329 58885d79 Iustin Pop
    """
3330 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
3331 a87b4824 Michael Hanselmann
      self.fd.close()
3332 a87b4824 Michael Hanselmann
      self.fd = None
3333 a87b4824 Michael Hanselmann
3334 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
3335 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
3336 aa74b828 Michael Hanselmann

3337 aa74b828 Michael Hanselmann
    @type flag: int
3338 58885d79 Iustin Pop
    @param flag: operation flag
3339 aa74b828 Michael Hanselmann
    @type blocking: bool
3340 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
3341 aa74b828 Michael Hanselmann
    @type timeout: None or float
3342 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
3343 aa74b828 Michael Hanselmann
                    non-blocking mode).
3344 aa74b828 Michael Hanselmann
    @type errmsg: string
3345 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
3346 aa74b828 Michael Hanselmann

3347 aa74b828 Michael Hanselmann
    """
3348 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
3349 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
3350 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
3351 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
3352 a87b4824 Michael Hanselmann
3353 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
3354 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
3355 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
3356 a87b4824 Michael Hanselmann
3357 cc4c9b91 Michael Hanselmann
    if timeout is None:
3358 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
3359 cc4c9b91 Michael Hanselmann
    else:
3360 cc4c9b91 Michael Hanselmann
      try:
3361 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
3362 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
3363 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
3364 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
3365 aa74b828 Michael Hanselmann
3366 cc4c9b91 Michael Hanselmann
  @staticmethod
3367 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
3368 cc4c9b91 Michael Hanselmann
    try:
3369 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
3370 cc4c9b91 Michael Hanselmann
    except IOError, err:
3371 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
3372 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
3373 31892b4c Michael Hanselmann
3374 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
3375 cc4c9b91 Michael Hanselmann
      raise
3376 aa74b828 Michael Hanselmann
3377 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
3378 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
3379 a87b4824 Michael Hanselmann

3380 58885d79 Iustin Pop
    @type blocking: boolean
3381 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3382 58885d79 Iustin Pop
        can lock the file or return immediately
3383 58885d79 Iustin Pop
    @type timeout: int or None
3384 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3385 58885d79 Iustin Pop
        (in blocking mode)
3386 58885d79 Iustin Pop

3387 a87b4824 Michael Hanselmann
    """
3388 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
3389 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
3390 a87b4824 Michael Hanselmann
3391 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
3392 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
3393 a87b4824 Michael Hanselmann

3394 58885d79 Iustin Pop
    @type blocking: boolean
3395 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3396 58885d79 Iustin Pop
        can lock the file or return immediately
3397 58885d79 Iustin Pop
    @type timeout: int or None
3398 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3399 58885d79 Iustin Pop
        (in blocking mode)
3400 58885d79 Iustin Pop

3401 a87b4824 Michael Hanselmann
    """
3402 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
3403 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
3404 a87b4824 Michael Hanselmann
3405 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
3406 a87b4824 Michael Hanselmann
    """Unlocks the file.
3407 a87b4824 Michael Hanselmann

3408 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
3409 58885d79 Iustin Pop
    operation::
3410 58885d79 Iustin Pop

3411 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
3412 58885d79 Iustin Pop
      operations.
3413 58885d79 Iustin Pop

3414 58885d79 Iustin Pop
    @type blocking: boolean
3415 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
3416 58885d79 Iustin Pop
        can lock the file or return immediately
3417 58885d79 Iustin Pop
    @type timeout: int or None
3418 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
3419 58885d79 Iustin Pop
        (in blocking mode)
3420 a87b4824 Michael Hanselmann

3421 a87b4824 Michael Hanselmann
    """
3422 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
3423 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
3424 a87b4824 Michael Hanselmann
3425 a87b4824 Michael Hanselmann
3426 339be5a8 Michael Hanselmann
class LineSplitter:
3427 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
3428 339be5a8 Michael Hanselmann

3429 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
3430 339be5a8 Michael Hanselmann

3431 339be5a8 Michael Hanselmann
  """
3432 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
3433 339be5a8 Michael Hanselmann
    """Initializes this class.
3434 339be5a8 Michael Hanselmann

3435 339be5a8 Michael Hanselmann
    @type line_fn: callable
3436 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
3437 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
3438 339be5a8 Michael Hanselmann

3439 339be5a8 Michael Hanselmann
    """
3440 339be5a8 Michael Hanselmann
    assert callable(line_fn)
3441 339be5a8 Michael Hanselmann
3442 339be5a8 Michael Hanselmann
    if args:
3443 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
3444 339be5a8 Michael Hanselmann
      self._line_fn = \
3445 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
3446 339be5a8 Michael Hanselmann
    else:
3447 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
3448 339be5a8 Michael Hanselmann
3449 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
3450 339be5a8 Michael Hanselmann
    self._buffer = ""
3451 339be5a8 Michael Hanselmann
3452 339be5a8 Michael Hanselmann
  def write(self, data):
3453 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
3454 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
3455 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
3456 339be5a8 Michael Hanselmann
3457 339be5a8 Michael Hanselmann
  def flush(self):
3458 339be5a8 Michael Hanselmann
    while self._lines:
3459 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
3460 339be5a8 Michael Hanselmann
3461 339be5a8 Michael Hanselmann
  def close(self):
3462 339be5a8 Michael Hanselmann
    self.flush()
3463 339be5a8 Michael Hanselmann
    if self._buffer:
3464 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
3465 339be5a8 Michael Hanselmann
3466 339be5a8 Michael Hanselmann
3467 451575de Guido Trotter
def SignalHandled(signums):
3468 451575de Guido Trotter
  """Signal Handled decoration.
3469 451575de Guido Trotter

3470 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
3471 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
3472 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
3473 451575de Guido Trotter
  objects as values.
3474 451575de Guido Trotter

3475 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
3476 451575de Guido Trotter
  with different handlers.
3477 451575de Guido Trotter

3478 451575de Guido Trotter
  @type signums: list
3479 451575de Guido Trotter
  @param signums: signals to intercept
3480 451575de Guido Trotter

3481 451575de Guido Trotter
  """
3482 451575de Guido Trotter
  def wrap(fn):
3483 451575de Guido Trotter
    def sig_function(*args, **kwargs):
3484 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
3485 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
3486 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
3487 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
3488 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
3489 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
3490 451575de Guido Trotter
      else:
3491 451575de Guido Trotter
        signal_handlers = {}
3492 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
3493 451575de Guido Trotter
      sighandler = SignalHandler(signums)
3494 451575de Guido Trotter
      try:
3495 451575de Guido Trotter
        for sig in signums:
3496 451575de Guido Trotter
          signal_handlers[sig] = sighandler
3497 451575de Guido Trotter
        return fn(*args, **kwargs)
3498 451575de Guido Trotter
      finally:
3499 451575de Guido Trotter
        sighandler.Reset()
3500 451575de Guido Trotter
    return sig_function
3501 451575de Guido Trotter
  return wrap
3502 451575de Guido Trotter
3503 451575de Guido Trotter
3504 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
3505 b9768937 Michael Hanselmann
  try:
3506 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
3507 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
3508 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
3509 b9768937 Michael Hanselmann
  except AttributeError:
3510 b9768937 Michael Hanselmann
    # Not supported
3511 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, _): # pylint: disable-msg=R0201
3512 b9768937 Michael Hanselmann
      return -1
3513 b9768937 Michael Hanselmann
  else:
3514 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
3515 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
3516 b9768937 Michael Hanselmann
3517 b9768937 Michael Hanselmann
  def __init__(self):
3518 b9768937 Michael Hanselmann
    """Initializes this class.
3519 b9768937 Michael Hanselmann

3520 b9768937 Michael Hanselmann
    """
3521 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
3522 b9768937 Michael Hanselmann
3523 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
3524 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
3525 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
3526 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
3527 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
3528 b9768937 Michael Hanselmann
3529 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
3530 b9768937 Michael Hanselmann
3531 b9768937 Michael Hanselmann
    # Utility functions
3532 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
3533 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
3534 b9768937 Michael Hanselmann
3535 b9768937 Michael Hanselmann
  def Reset(self):
3536 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
3537 b9768937 Michael Hanselmann

3538 b9768937 Michael Hanselmann
    """
3539 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
3540 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
3541 b9768937 Michael Hanselmann
      self._previous = None
3542 b9768937 Michael Hanselmann
3543 b9768937 Michael Hanselmann
  def Notify(self):
3544 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
3545 b9768937 Michael Hanselmann

3546 b9768937 Michael Hanselmann
    """
3547 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
3548 b9768937 Michael Hanselmann
3549 b9768937 Michael Hanselmann
  def __del__(self):
3550 b9768937 Michael Hanselmann
    """Called before object deletion.
3551 b9768937 Michael Hanselmann

3552 b9768937 Michael Hanselmann
    """
3553 b9768937 Michael Hanselmann
    self.Reset()
3554 b9768937 Michael Hanselmann
3555 b9768937 Michael Hanselmann
3556 de499029 Michael Hanselmann
class SignalHandler(object):
3557 de499029 Michael Hanselmann
  """Generic signal handler class.
3558 de499029 Michael Hanselmann

3559 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3560 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3561 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3562 58885d79 Iustin Pop
  signal was sent.
3563 58885d79 Iustin Pop

3564 58885d79 Iustin Pop
  @type signum: list
3565 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3566 58885d79 Iustin Pop
  @type called: boolean
3567 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3568 de499029 Michael Hanselmann

3569 de499029 Michael Hanselmann
  """
3570 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
3571 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3572 de499029 Michael Hanselmann

3573 58885d79 Iustin Pop
    @type signum: int or list of ints
3574 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3575 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
3576 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
3577 de499029 Michael Hanselmann

3578 de499029 Michael Hanselmann
    """
3579 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
3580 92b61ec7 Michael Hanselmann
3581 6c52849e Guido Trotter
    self.signum = set(signum)
3582 de499029 Michael Hanselmann
    self.called = False
3583 de499029 Michael Hanselmann
3584 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
3585 b9768937 Michael Hanselmann
    self._wakeup = wakeup
3586 92b61ec7 Michael Hanselmann
3587 de499029 Michael Hanselmann
    self._previous = {}
3588 de499029 Michael Hanselmann
    try:
3589 de499029 Michael Hanselmann
      for signum in self.signum:
3590 de499029 Michael Hanselmann
        # Setup handler
3591 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3592 de499029 Michael Hanselmann
        try:
3593 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3594 de499029 Michael Hanselmann
        except:
3595 de499029 Michael Hanselmann
          # Restore previous handler
3596 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3597 de499029 Michael Hanselmann
          raise
3598 de499029 Michael Hanselmann
    except:
3599 de499029 Michael Hanselmann
      # Reset all handlers
3600 de499029 Michael Hanselmann
      self.Reset()
3601 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3602 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3603 de499029 Michael Hanselmann
      raise
3604 de499029 Michael Hanselmann
3605 de499029 Michael Hanselmann
  def __del__(self):
3606 de499029 Michael Hanselmann
    self.Reset()
3607 de499029 Michael Hanselmann
3608 de499029 Michael Hanselmann
  def Reset(self):
3609 de499029 Michael Hanselmann
    """Restore previous handler.
3610 de499029 Michael Hanselmann

3611 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3612 58885d79 Iustin Pop

3613 de499029 Michael Hanselmann
    """
3614 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3615 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3616 de499029 Michael Hanselmann
      # If successful, remove from dict
3617 de499029 Michael Hanselmann
      del self._previous[signum]
3618 de499029 Michael Hanselmann
3619 de499029 Michael Hanselmann
  def Clear(self):
3620 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3621 de499029 Michael Hanselmann

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

3624 de499029 Michael Hanselmann
    """
3625 de499029 Michael Hanselmann
    self.called = False
3626 de499029 Michael Hanselmann
3627 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
3628 de499029 Michael Hanselmann
    """Actual signal handling function.
3629 de499029 Michael Hanselmann

3630 de499029 Michael Hanselmann
    """
3631 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3632 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3633 de499029 Michael Hanselmann
    self.called = True
3634 a2d2e1a7 Iustin Pop
3635 b9768937 Michael Hanselmann
    if self._wakeup:
3636 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
3637 b9768937 Michael Hanselmann
      self._wakeup.Notify()
3638 b9768937 Michael Hanselmann
3639 92b61ec7 Michael Hanselmann
    if self._handler_fn:
3640 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
3641 92b61ec7 Michael Hanselmann
3642 a2d2e1a7 Iustin Pop
3643 a2d2e1a7 Iustin Pop
class FieldSet(object):
3644 a2d2e1a7 Iustin Pop
  """A simple field set.
3645 a2d2e1a7 Iustin Pop

3646 a2d2e1a7 Iustin Pop
  Among the features are:
3647 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3648 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3649 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3650 a2d2e1a7 Iustin Pop

3651 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3652 a2d2e1a7 Iustin Pop

3653 a2d2e1a7 Iustin Pop
  """
3654 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3655 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3656 a2d2e1a7 Iustin Pop
3657 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3658 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3659 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3660 a2d2e1a7 Iustin Pop
3661 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3662 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3663 a2d2e1a7 Iustin Pop

3664 a2d2e1a7 Iustin Pop
    @type field: str
3665 a2d2e1a7 Iustin Pop
    @param field: the string to match
3666 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3667 a2d2e1a7 Iustin Pop

3668 a2d2e1a7 Iustin Pop
    """
3669 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3670 a2d2e1a7 Iustin Pop
      return m
3671 6c881c52 Iustin Pop
    return None
3672 a2d2e1a7 Iustin Pop
3673 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3674 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3675 a2d2e1a7 Iustin Pop

3676 a2d2e1a7 Iustin Pop
    @type items: list
3677 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3678 a2d2e1a7 Iustin Pop
    @rtype: list
3679 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3680 a2d2e1a7 Iustin Pop

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