Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 4b6fa0bf

History | View | Annotate | Download (82.4 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 a8083063 Iustin Pop
import time
32 113b55aa Iustin Pop
import subprocess
33 a8083063 Iustin Pop
import re
34 a8083063 Iustin Pop
import socket
35 a8083063 Iustin Pop
import tempfile
36 a8083063 Iustin Pop
import shutil
37 4ca1b175 Alexander Schreiber
import errno
38 2f8b60b3 Iustin Pop
import pwd
39 78feb6fb Guido Trotter
import itertools
40 9c233417 Iustin Pop
import select
41 9c233417 Iustin Pop
import fcntl
42 8f765069 Iustin Pop
import resource
43 bb698c1f Iustin Pop
import logging
44 551b6283 Iustin Pop
import logging.handlers
45 de499029 Michael Hanselmann
import signal
46 27e46076 Michael Hanselmann
import datetime
47 27e46076 Michael Hanselmann
import calendar
48 339be5a8 Michael Hanselmann
import collections
49 f93f2016 Michael Hanselmann
import struct
50 f93f2016 Michael Hanselmann
import IN
51 9c233417 Iustin Pop
52 9c233417 Iustin Pop
from cStringIO import StringIO
53 a8083063 Iustin Pop
54 7ffe8fba Carlos Valiente
try:
55 7ffe8fba Carlos Valiente
  from hashlib import sha1
56 7ffe8fba Carlos Valiente
except ImportError:
57 7ffe8fba Carlos Valiente
  import sha
58 7ffe8fba Carlos Valiente
  sha1 = sha.new
59 7ffe8fba Carlos Valiente
60 4b6fa0bf Luca Bigliardi
try:
61 4b6fa0bf Luca Bigliardi
  import ctypes
62 4b6fa0bf Luca Bigliardi
except ImportError:
63 4b6fa0bf Luca Bigliardi
  ctypes = None
64 4b6fa0bf Luca Bigliardi
65 a8083063 Iustin Pop
from ganeti import errors
66 3aecd2c7 Iustin Pop
from ganeti import constants
67 a8083063 Iustin Pop
68 16abfbc2 Alexander Schreiber
69 a8083063 Iustin Pop
_locksheld = []
70 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
71 a8083063 Iustin Pop
72 e67bd559 Michael Hanselmann
debug_locks = False
73 58885d79 Iustin Pop
74 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
75 b74159ee Iustin Pop
no_fork = False
76 f362096f Iustin Pop
77 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
78 13998ef2 Michael Hanselmann
79 f93f2016 Michael Hanselmann
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
80 f93f2016 Michael Hanselmann
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
81 f93f2016 Michael Hanselmann
#
82 f93f2016 Michael Hanselmann
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
83 f93f2016 Michael Hanselmann
# pid_t to "int".
84 f93f2016 Michael Hanselmann
#
85 f93f2016 Michael Hanselmann
# IEEE Std 1003.1-2008:
86 f93f2016 Michael Hanselmann
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
87 f93f2016 Michael Hanselmann
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
88 f93f2016 Michael Hanselmann
_STRUCT_UCRED = "iII"
89 f93f2016 Michael Hanselmann
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
90 f93f2016 Michael Hanselmann
91 4b6fa0bf Luca Bigliardi
# Flags for mlockall() (from bits/mman.h)
92 4b6fa0bf Luca Bigliardi
_MCL_CURRENT = 1
93 4b6fa0bf Luca Bigliardi
_MCL_FUTURE = 2
94 4b6fa0bf Luca Bigliardi
95 7c0d6283 Michael Hanselmann
96 a8083063 Iustin Pop
class RunResult(object):
97 58885d79 Iustin Pop
  """Holds the result of running external programs.
98 58885d79 Iustin Pop

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

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

141 a8083063 Iustin Pop
    """
142 a8083063 Iustin Pop
    return self.stdout + self.stderr
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
147 bf4daac9 Guido Trotter
def RunCmd(cmd, env=None, output=None, cwd='/', reset_env=False):
148 a8083063 Iustin Pop
  """Execute a (shell) command.
149 a8083063 Iustin Pop

150 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
151 a8083063 Iustin Pop
  closed.
152 a8083063 Iustin Pop

153 bf4daac9 Guido Trotter
  @type cmd: string or list
154 36117c2b Iustin Pop
  @param cmd: Command to run
155 2557ff82 Guido Trotter
  @type env: dict
156 58885d79 Iustin Pop
  @param env: Additional environment
157 36117c2b Iustin Pop
  @type output: str
158 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
159 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
160 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
161 8797df43 Iustin Pop
  @type cwd: string
162 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
163 8797df43 Iustin Pop
      directory for the command; the default will be /
164 bf4daac9 Guido Trotter
  @type reset_env: boolean
165 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
166 36117c2b Iustin Pop
  @rtype: L{RunResult}
167 58885d79 Iustin Pop
  @return: RunResult instance
168 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
  """
171 b74159ee Iustin Pop
  if no_fork:
172 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
173 b74159ee Iustin Pop
174 a8083063 Iustin Pop
  if isinstance(cmd, list):
175 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
176 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
177 113b55aa Iustin Pop
    shell = False
178 113b55aa Iustin Pop
  else:
179 113b55aa Iustin Pop
    strcmd = cmd
180 113b55aa Iustin Pop
    shell = True
181 bb698c1f Iustin Pop
  logging.debug("RunCmd '%s'", strcmd)
182 2557ff82 Guido Trotter
183 bf4daac9 Guido Trotter
  if not reset_env:
184 bf4daac9 Guido Trotter
    cmd_env = os.environ.copy()
185 bf4daac9 Guido Trotter
    cmd_env["LC_ALL"] = "C"
186 bf4daac9 Guido Trotter
  else:
187 bf4daac9 Guido Trotter
    cmd_env = {}
188 bf4daac9 Guido Trotter
189 2557ff82 Guido Trotter
  if env is not None:
190 2557ff82 Guido Trotter
    cmd_env.update(env)
191 2557ff82 Guido Trotter
192 c803b052 Iustin Pop
  try:
193 c803b052 Iustin Pop
    if output is None:
194 c803b052 Iustin Pop
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
195 c803b052 Iustin Pop
    else:
196 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
197 c803b052 Iustin Pop
      out = err = ""
198 c803b052 Iustin Pop
  except OSError, err:
199 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
200 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
201 c803b052 Iustin Pop
                               (strcmd, err))
202 c803b052 Iustin Pop
    else:
203 c803b052 Iustin Pop
      raise
204 36117c2b Iustin Pop
205 36117c2b Iustin Pop
  if status >= 0:
206 36117c2b Iustin Pop
    exitcode = status
207 36117c2b Iustin Pop
    signal_ = None
208 36117c2b Iustin Pop
  else:
209 36117c2b Iustin Pop
    exitcode = None
210 36117c2b Iustin Pop
    signal_ = -status
211 36117c2b Iustin Pop
212 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
213 36117c2b Iustin Pop
214 ae59efea Michael Hanselmann
215 8797df43 Iustin Pop
def _RunCmdPipe(cmd, env, via_shell, cwd):
216 36117c2b Iustin Pop
  """Run a command and return its output.
217 36117c2b Iustin Pop

218 36117c2b Iustin Pop
  @type  cmd: string or list
219 36117c2b Iustin Pop
  @param cmd: Command to run
220 36117c2b Iustin Pop
  @type env: dict
221 36117c2b Iustin Pop
  @param env: The environment to use
222 36117c2b Iustin Pop
  @type via_shell: bool
223 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
224 8797df43 Iustin Pop
  @type cwd: string
225 8797df43 Iustin Pop
  @param cwd: the working directory for the program
226 36117c2b Iustin Pop
  @rtype: tuple
227 36117c2b Iustin Pop
  @return: (out, err, status)
228 36117c2b Iustin Pop

229 36117c2b Iustin Pop
  """
230 9c233417 Iustin Pop
  poller = select.poll()
231 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
232 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
233 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
234 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
235 8797df43 Iustin Pop
                           close_fds=True, env=env,
236 8797df43 Iustin Pop
                           cwd=cwd)
237 113b55aa Iustin Pop
238 113b55aa Iustin Pop
  child.stdin.close()
239 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
240 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
241 9c233417 Iustin Pop
  out = StringIO()
242 9c233417 Iustin Pop
  err = StringIO()
243 9c233417 Iustin Pop
  fdmap = {
244 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
245 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
246 9c233417 Iustin Pop
    }
247 9c233417 Iustin Pop
  for fd in fdmap:
248 9c233417 Iustin Pop
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
249 9c233417 Iustin Pop
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
250 9c233417 Iustin Pop
251 9c233417 Iustin Pop
  while fdmap:
252 bf988c29 Guido Trotter
    try:
253 bf988c29 Guido Trotter
      pollresult = poller.poll()
254 bf988c29 Guido Trotter
    except EnvironmentError, eerr:
255 bf988c29 Guido Trotter
      if eerr.errno == errno.EINTR:
256 bf988c29 Guido Trotter
        continue
257 bf988c29 Guido Trotter
      raise
258 bf988c29 Guido Trotter
    except select.error, serr:
259 bf988c29 Guido Trotter
      if serr[0] == errno.EINTR:
260 bf988c29 Guido Trotter
        continue
261 bf988c29 Guido Trotter
      raise
262 bf988c29 Guido Trotter
263 bf988c29 Guido Trotter
    for fd, event in pollresult:
264 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
265 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
266 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
267 9c233417 Iustin Pop
        if not data:
268 9c233417 Iustin Pop
          poller.unregister(fd)
269 9c233417 Iustin Pop
          del fdmap[fd]
270 9c233417 Iustin Pop
          continue
271 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
272 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
273 9c233417 Iustin Pop
          event & select.POLLERR):
274 9c233417 Iustin Pop
        poller.unregister(fd)
275 9c233417 Iustin Pop
        del fdmap[fd]
276 9c233417 Iustin Pop
277 9c233417 Iustin Pop
  out = out.getvalue()
278 9c233417 Iustin Pop
  err = err.getvalue()
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
  status = child.wait()
281 36117c2b Iustin Pop
  return out, err, status
282 a8083063 Iustin Pop
283 36117c2b Iustin Pop
284 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
285 36117c2b Iustin Pop
  """Run a command and save its output to a file.
286 36117c2b Iustin Pop

287 36117c2b Iustin Pop
  @type  cmd: string or list
288 36117c2b Iustin Pop
  @param cmd: Command to run
289 36117c2b Iustin Pop
  @type env: dict
290 36117c2b Iustin Pop
  @param env: The environment to use
291 36117c2b Iustin Pop
  @type via_shell: bool
292 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
293 36117c2b Iustin Pop
  @type output: str
294 36117c2b Iustin Pop
  @param output: the filename in which to save the output
295 8797df43 Iustin Pop
  @type cwd: string
296 8797df43 Iustin Pop
  @param cwd: the working directory for the program
297 36117c2b Iustin Pop
  @rtype: int
298 36117c2b Iustin Pop
  @return: the exit status
299 36117c2b Iustin Pop

300 36117c2b Iustin Pop
  """
301 36117c2b Iustin Pop
  fh = open(output, "a")
302 36117c2b Iustin Pop
  try:
303 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
304 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
305 36117c2b Iustin Pop
                             stdout=fh,
306 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
307 8797df43 Iustin Pop
                             close_fds=True, env=env,
308 8797df43 Iustin Pop
                             cwd=cwd)
309 36117c2b Iustin Pop
310 36117c2b Iustin Pop
    child.stdin.close()
311 36117c2b Iustin Pop
    status = child.wait()
312 36117c2b Iustin Pop
  finally:
313 36117c2b Iustin Pop
    fh.close()
314 36117c2b Iustin Pop
  return status
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
317 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
318 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
319 6bb65e3a Guido Trotter

320 6bb65e3a Guido Trotter
  @type dir_name: string
321 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
322 6bb65e3a Guido Trotter
  @type env: dict
323 6bb65e3a Guido Trotter
  @param env: The environment to use
324 6bb65e3a Guido Trotter
  @type reset_env: boolean
325 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
326 6bb65e3a Guido Trotter
  @rtype: list of tuples
327 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
328 6bb65e3a Guido Trotter

329 6bb65e3a Guido Trotter
  """
330 6bb65e3a Guido Trotter
  rr = []
331 6bb65e3a Guido Trotter
332 6bb65e3a Guido Trotter
  try:
333 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
334 6bb65e3a Guido Trotter
  except OSError, err:
335 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
336 6bb65e3a Guido Trotter
    return rr
337 6bb65e3a Guido Trotter
338 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
339 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
340 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
341 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
342 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
343 6bb65e3a Guido Trotter
    else:
344 6bb65e3a Guido Trotter
      try:
345 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
346 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
347 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
348 6bb65e3a Guido Trotter
      else:
349 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
350 6bb65e3a Guido Trotter
351 6bb65e3a Guido Trotter
  return rr
352 6bb65e3a Guido Trotter
353 6bb65e3a Guido Trotter
354 f93f2016 Michael Hanselmann
def GetSocketCredentials(sock):
355 f93f2016 Michael Hanselmann
  """Returns the credentials of the foreign process connected to a socket.
356 f93f2016 Michael Hanselmann

357 f93f2016 Michael Hanselmann
  @param sock: Unix socket
358 f93f2016 Michael Hanselmann
  @rtype: tuple; (number, number, number)
359 f93f2016 Michael Hanselmann
  @return: The PID, UID and GID of the connected foreign process.
360 f93f2016 Michael Hanselmann

361 f93f2016 Michael Hanselmann
  """
362 f93f2016 Michael Hanselmann
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
363 f93f2016 Michael Hanselmann
                             _STRUCT_UCRED_SIZE)
364 f93f2016 Michael Hanselmann
  return struct.unpack(_STRUCT_UCRED, peercred)
365 f93f2016 Michael Hanselmann
366 f93f2016 Michael Hanselmann
367 a8083063 Iustin Pop
def RemoveFile(filename):
368 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
369 a8083063 Iustin Pop

370 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
371 a8083063 Iustin Pop
  errors are passed.
372 a8083063 Iustin Pop

373 58885d79 Iustin Pop
  @type filename: str
374 58885d79 Iustin Pop
  @param filename: the file to be removed
375 58885d79 Iustin Pop

376 a8083063 Iustin Pop
  """
377 a8083063 Iustin Pop
  try:
378 a8083063 Iustin Pop
    os.unlink(filename)
379 a8083063 Iustin Pop
  except OSError, err:
380 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
381 a8083063 Iustin Pop
      raise
382 a8083063 Iustin Pop
383 a8083063 Iustin Pop
384 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
385 6e797216 Michael Hanselmann
  """Renames a file.
386 6e797216 Michael Hanselmann

387 6e797216 Michael Hanselmann
  @type old: string
388 6e797216 Michael Hanselmann
  @param old: Original path
389 6e797216 Michael Hanselmann
  @type new: string
390 6e797216 Michael Hanselmann
  @param new: New path
391 6e797216 Michael Hanselmann
  @type mkdir: bool
392 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
393 6e797216 Michael Hanselmann
  @type mkdir_mode: int
394 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
395 6e797216 Michael Hanselmann

396 6e797216 Michael Hanselmann
  """
397 6e797216 Michael Hanselmann
  try:
398 6e797216 Michael Hanselmann
    return os.rename(old, new)
399 6e797216 Michael Hanselmann
  except OSError, err:
400 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
401 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
402 6e797216 Michael Hanselmann
    # as efficient.
403 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
404 6e797216 Michael Hanselmann
      # Create directory and try again
405 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
406 a426508d Michael Hanselmann
407 6e797216 Michael Hanselmann
      return os.rename(old, new)
408 a426508d Michael Hanselmann
409 6e797216 Michael Hanselmann
    raise
410 6e797216 Michael Hanselmann
411 6e797216 Michael Hanselmann
412 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
413 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
414 76e5f8b5 Michael Hanselmann

415 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
416 76e5f8b5 Michael Hanselmann
  before Python 2.5.
417 76e5f8b5 Michael Hanselmann

418 76e5f8b5 Michael Hanselmann
  """
419 76e5f8b5 Michael Hanselmann
  try:
420 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
421 76e5f8b5 Michael Hanselmann
  except OSError, err:
422 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
423 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
424 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
425 76e5f8b5 Michael Hanselmann
      raise
426 76e5f8b5 Michael Hanselmann
427 76e5f8b5 Michael Hanselmann
428 055f822b Michael Hanselmann
def ResetTempfileModule():
429 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
430 055f822b Michael Hanselmann

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

437 055f822b Michael Hanselmann
  """
438 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
439 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
440 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
441 055f822b Michael Hanselmann
    try:
442 055f822b Michael Hanselmann
      # Reset random name generator
443 055f822b Michael Hanselmann
      tempfile._name_sequence = None
444 055f822b Michael Hanselmann
    finally:
445 055f822b Michael Hanselmann
      tempfile._once_lock.release()
446 055f822b Michael Hanselmann
  else:
447 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
448 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
449 055f822b Michael Hanselmann
450 055f822b Michael Hanselmann
451 a8083063 Iustin Pop
def _FingerprintFile(filename):
452 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
455 a8083063 Iustin Pop
  instead.
456 a8083063 Iustin Pop

457 58885d79 Iustin Pop
  @type filename: str
458 58885d79 Iustin Pop
  @param filename: the filename to checksum
459 58885d79 Iustin Pop
  @rtype: str
460 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
461 58885d79 Iustin Pop
      of the file
462 a8083063 Iustin Pop

463 a8083063 Iustin Pop
  """
464 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
465 a8083063 Iustin Pop
    return None
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
  f = open(filename)
468 a8083063 Iustin Pop
469 7ffe8fba Carlos Valiente
  fp = sha1()
470 a8083063 Iustin Pop
  while True:
471 a8083063 Iustin Pop
    data = f.read(4096)
472 a8083063 Iustin Pop
    if not data:
473 a8083063 Iustin Pop
      break
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
    fp.update(data)
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
  return fp.hexdigest()
478 a8083063 Iustin Pop
479 a8083063 Iustin Pop
480 a8083063 Iustin Pop
def FingerprintFiles(files):
481 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
482 a8083063 Iustin Pop

483 58885d79 Iustin Pop
  @type files: list
484 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
485 58885d79 Iustin Pop
  @rtype: dict
486 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
487 58885d79 Iustin Pop
      existing files
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
  """
490 a8083063 Iustin Pop
  ret = {}
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
  for filename in files:
493 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
494 a8083063 Iustin Pop
    if cksum:
495 a8083063 Iustin Pop
      ret[filename] = cksum
496 a8083063 Iustin Pop
497 a8083063 Iustin Pop
  return ret
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
500 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
501 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
502 a5728081 Guido Trotter

503 a5728081 Guido Trotter
  @type target: dict
504 a5728081 Guido Trotter
  @param target: the dict to update
505 a5728081 Guido Trotter
  @type key_types: dict
506 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
507 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
508 a5728081 Guido Trotter
  @type allowed_values: list
509 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
510 a5728081 Guido Trotter

511 a5728081 Guido Trotter
  """
512 a5728081 Guido Trotter
  if allowed_values is None:
513 a5728081 Guido Trotter
    allowed_values = []
514 a5728081 Guido Trotter
515 8b46606c Guido Trotter
  if not isinstance(target, dict):
516 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
517 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
518 8b46606c Guido Trotter
519 a5728081 Guido Trotter
  for key in target:
520 a5728081 Guido Trotter
    if key not in key_types:
521 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
522 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
523 a5728081 Guido Trotter
524 a5728081 Guido Trotter
    if target[key] in allowed_values:
525 a5728081 Guido Trotter
      continue
526 a5728081 Guido Trotter
527 29921401 Iustin Pop
    ktype = key_types[key]
528 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
529 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
530 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
531 a5728081 Guido Trotter
532 29921401 Iustin Pop
    if ktype == constants.VTYPE_STRING:
533 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
534 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
535 a5728081 Guido Trotter
          target[key] = ''
536 a5728081 Guido Trotter
        else:
537 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
538 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
539 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
540 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
541 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
542 a5728081 Guido Trotter
          target[key] = False
543 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
544 a5728081 Guido Trotter
          target[key] = True
545 a5728081 Guido Trotter
        else:
546 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
547 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
548 a5728081 Guido Trotter
      elif target[key]:
549 a5728081 Guido Trotter
        target[key] = True
550 a5728081 Guido Trotter
      else:
551 a5728081 Guido Trotter
        target[key] = False
552 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
553 a5728081 Guido Trotter
      try:
554 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
555 a5728081 Guido Trotter
      except errors.UnitParseError, err:
556 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
557 a5728081 Guido Trotter
              (key, target[key], err)
558 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
559 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
560 a5728081 Guido Trotter
      try:
561 a5728081 Guido Trotter
        target[key] = int(target[key])
562 a5728081 Guido Trotter
      except (ValueError, TypeError):
563 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
564 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
565 a5728081 Guido Trotter
566 a5728081 Guido Trotter
567 a8083063 Iustin Pop
def IsProcessAlive(pid):
568 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
569 a8083063 Iustin Pop

570 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
571 44bf25ff Iustin Pop
      will be returned as alive
572 58885d79 Iustin Pop
  @type pid: int
573 58885d79 Iustin Pop
  @param pid: the process ID to check
574 58885d79 Iustin Pop
  @rtype: boolean
575 58885d79 Iustin Pop
  @return: True if the process exists
576 a8083063 Iustin Pop

577 a8083063 Iustin Pop
  """
578 5ef5ea45 Guido Trotter
  def _TryStat(name):
579 5ef5ea45 Guido Trotter
    try:
580 5ef5ea45 Guido Trotter
      os.stat(name)
581 5ef5ea45 Guido Trotter
      return True
582 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
583 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
584 5ef5ea45 Guido Trotter
        return False
585 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
586 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
587 5ef5ea45 Guido Trotter
      raise
588 5ef5ea45 Guido Trotter
589 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
590 d9f311d7 Iustin Pop
  if pid <= 0:
591 d9f311d7 Iustin Pop
    return False
592 d9f311d7 Iustin Pop
593 5ef5ea45 Guido Trotter
  proc_entry = "/proc/%d/status" % pid
594 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
595 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
596 a8083063 Iustin Pop
  try:
597 5ef5ea45 Guido Trotter
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5, args=[proc_entry])
598 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
599 5ef5ea45 Guido Trotter
    err.RaiseInner()
600 a8083063 Iustin Pop
601 a8083063 Iustin Pop
602 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
603 58885d79 Iustin Pop
  """Read a pid from a file.
604 fee80e90 Guido Trotter

605 58885d79 Iustin Pop
  @type  pidfile: string
606 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
607 58885d79 Iustin Pop
  @rtype: int
608 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
609 d9f311d7 Iustin Pop
           otherwise 0
610 fee80e90 Guido Trotter

611 fee80e90 Guido Trotter
  """
612 fee80e90 Guido Trotter
  try:
613 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
614 d9f311d7 Iustin Pop
  except EnvironmentError, err:
615 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
616 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
617 d9f311d7 Iustin Pop
    return 0
618 fee80e90 Guido Trotter
619 fee80e90 Guido Trotter
  try:
620 13998ef2 Michael Hanselmann
    pid = int(raw_data)
621 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
622 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
623 d9f311d7 Iustin Pop
    return 0
624 fee80e90 Guido Trotter
625 d9f311d7 Iustin Pop
  return pid
626 fee80e90 Guido Trotter
627 fee80e90 Guido Trotter
628 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
629 a8083063 Iustin Pop
  """Try to match a name against a list.
630 a8083063 Iustin Pop

631 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
632 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
633 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
634 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
635 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
636 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
637 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
638 a8083063 Iustin Pop

639 58885d79 Iustin Pop
  @type key: str
640 58885d79 Iustin Pop
  @param key: the name to be searched
641 58885d79 Iustin Pop
  @type name_list: list
642 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
643 256eb94b Guido Trotter
  @type case_sensitive: boolean
644 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
645 a8083063 Iustin Pop

646 58885d79 Iustin Pop
  @rtype: None or str
647 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
648 58885d79 Iustin Pop
      otherwise the element from the list which matches
649 a8083063 Iustin Pop

650 a8083063 Iustin Pop
  """
651 3a541d90 Iustin Pop
  if key in name_list:
652 3a541d90 Iustin Pop
    return key
653 256eb94b Guido Trotter
654 256eb94b Guido Trotter
  re_flags = 0
655 256eb94b Guido Trotter
  if not case_sensitive:
656 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
657 099c52ad Iustin Pop
    key = key.upper()
658 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
659 256eb94b Guido Trotter
  names_filtered = []
660 256eb94b Guido Trotter
  string_matches = []
661 256eb94b Guido Trotter
  for name in name_list:
662 256eb94b Guido Trotter
    if mo.match(name) is not None:
663 256eb94b Guido Trotter
      names_filtered.append(name)
664 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
665 256eb94b Guido Trotter
        string_matches.append(name)
666 256eb94b Guido Trotter
667 256eb94b Guido Trotter
  if len(string_matches) == 1:
668 256eb94b Guido Trotter
    return string_matches[0]
669 256eb94b Guido Trotter
  if len(names_filtered) == 1:
670 256eb94b Guido Trotter
    return names_filtered[0]
671 256eb94b Guido Trotter
  return None
672 a8083063 Iustin Pop
673 a8083063 Iustin Pop
674 bcf043c9 Iustin Pop
class HostInfo:
675 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
676 bcf043c9 Iustin Pop

677 bcf043c9 Iustin Pop
  """
678 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
679 26288e68 Iustin Pop
680 89e1fc26 Iustin Pop
  def __init__(self, name=None):
681 bcf043c9 Iustin Pop
    """Initialize the host name object.
682 bcf043c9 Iustin Pop

683 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
684 89e1fc26 Iustin Pop
    name.
685 bcf043c9 Iustin Pop

686 bcf043c9 Iustin Pop
    """
687 89e1fc26 Iustin Pop
    if name is None:
688 89e1fc26 Iustin Pop
      name = self.SysName()
689 89e1fc26 Iustin Pop
690 89e1fc26 Iustin Pop
    self.query = name
691 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
692 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
693 bcf043c9 Iustin Pop
694 c8a0948f Michael Hanselmann
  def ShortName(self):
695 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
696 c8a0948f Michael Hanselmann

697 c8a0948f Michael Hanselmann
    """
698 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
699 c8a0948f Michael Hanselmann
700 89e1fc26 Iustin Pop
  @staticmethod
701 89e1fc26 Iustin Pop
  def SysName():
702 89e1fc26 Iustin Pop
    """Return the current system's name.
703 bcf043c9 Iustin Pop

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

706 89e1fc26 Iustin Pop
    """
707 89e1fc26 Iustin Pop
    return socket.gethostname()
708 a8083063 Iustin Pop
709 89e1fc26 Iustin Pop
  @staticmethod
710 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
711 89e1fc26 Iustin Pop
    """Look up hostname
712 a8083063 Iustin Pop

713 58885d79 Iustin Pop
    @type hostname: str
714 58885d79 Iustin Pop
    @param hostname: hostname to look up
715 89e1fc26 Iustin Pop

716 58885d79 Iustin Pop
    @rtype: tuple
717 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
718 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
719 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
720 89e1fc26 Iustin Pop

721 89e1fc26 Iustin Pop
    """
722 89e1fc26 Iustin Pop
    try:
723 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
724 89e1fc26 Iustin Pop
    except socket.gaierror, err:
725 89e1fc26 Iustin Pop
      # hostname not found in DNS
726 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
727 a8083063 Iustin Pop
728 89e1fc26 Iustin Pop
    return result
729 a8083063 Iustin Pop
730 26288e68 Iustin Pop
  @classmethod
731 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
732 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
733 26288e68 Iustin Pop

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

738 26288e68 Iustin Pop
    """
739 26288e68 Iustin Pop
    hostname = hostname.lower()
740 26288e68 Iustin Pop
    if (not cls._VALID_NAME_RE.match(hostname) or
741 26288e68 Iustin Pop
        # double-dots, meaning empty label
742 26288e68 Iustin Pop
        ".." in hostname or
743 26288e68 Iustin Pop
        # empty initial label
744 26288e68 Iustin Pop
        hostname.startswith(".")):
745 26288e68 Iustin Pop
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
746 26288e68 Iustin Pop
                                 errors.ECODE_INVAL)
747 26288e68 Iustin Pop
    if hostname.endswith("."):
748 26288e68 Iustin Pop
      hostname = hostname.rstrip(".")
749 26288e68 Iustin Pop
    return hostname
750 26288e68 Iustin Pop
751 a8083063 Iustin Pop
752 104f4ca1 Iustin Pop
def GetHostInfo(name=None):
753 104f4ca1 Iustin Pop
  """Lookup host name and raise an OpPrereqError for failures"""
754 104f4ca1 Iustin Pop
755 104f4ca1 Iustin Pop
  try:
756 104f4ca1 Iustin Pop
    return HostInfo(name)
757 104f4ca1 Iustin Pop
  except errors.ResolverError, err:
758 104f4ca1 Iustin Pop
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
759 104f4ca1 Iustin Pop
                               (err[0], err[2]), errors.ECODE_RESOLVER)
760 104f4ca1 Iustin Pop
761 104f4ca1 Iustin Pop
762 a8083063 Iustin Pop
def ListVolumeGroups():
763 a8083063 Iustin Pop
  """List volume groups and their size
764 a8083063 Iustin Pop

765 58885d79 Iustin Pop
  @rtype: dict
766 58885d79 Iustin Pop
  @return:
767 58885d79 Iustin Pop
       Dictionary with keys volume name and values
768 58885d79 Iustin Pop
       the size of the volume
769 a8083063 Iustin Pop

770 a8083063 Iustin Pop
  """
771 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
772 a8083063 Iustin Pop
  result = RunCmd(command)
773 a8083063 Iustin Pop
  retval = {}
774 a8083063 Iustin Pop
  if result.failed:
775 a8083063 Iustin Pop
    return retval
776 a8083063 Iustin Pop
777 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
778 a8083063 Iustin Pop
    try:
779 a8083063 Iustin Pop
      name, size = line.split()
780 a8083063 Iustin Pop
      size = int(float(size))
781 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
782 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
783 a8083063 Iustin Pop
      continue
784 a8083063 Iustin Pop
785 a8083063 Iustin Pop
    retval[name] = size
786 a8083063 Iustin Pop
787 a8083063 Iustin Pop
  return retval
788 a8083063 Iustin Pop
789 a8083063 Iustin Pop
790 a8083063 Iustin Pop
def BridgeExists(bridge):
791 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
792 a8083063 Iustin Pop

793 58885d79 Iustin Pop
  @type bridge: str
794 58885d79 Iustin Pop
  @param bridge: the bridge name to check
795 58885d79 Iustin Pop
  @rtype: boolean
796 58885d79 Iustin Pop
  @return: True if it does
797 a8083063 Iustin Pop

798 a8083063 Iustin Pop
  """
799 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
802 a8083063 Iustin Pop
def NiceSort(name_list):
803 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
804 a8083063 Iustin Pop

805 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
806 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
807 58885d79 Iustin Pop
  'a11']}.
808 a8083063 Iustin Pop

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

813 58885d79 Iustin Pop
  @type name_list: list
814 58885d79 Iustin Pop
  @param name_list: the names to be sorted
815 58885d79 Iustin Pop
  @rtype: list
816 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
817 a8083063 Iustin Pop

818 a8083063 Iustin Pop
  """
819 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
820 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
821 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
822 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
823 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
824 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
825 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
826 a8083063 Iustin Pop
  def _TryInt(val):
827 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
828 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
829 a8083063 Iustin Pop
      return val
830 a8083063 Iustin Pop
    rval = int(val)
831 a8083063 Iustin Pop
    return rval
832 a8083063 Iustin Pop
833 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
834 a8083063 Iustin Pop
             for name in name_list]
835 a8083063 Iustin Pop
  to_sort.sort()
836 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
837 a8083063 Iustin Pop
838 a8083063 Iustin Pop
839 a8083063 Iustin Pop
def TryConvert(fn, val):
840 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
841 a8083063 Iustin Pop

842 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
843 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
844 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
845 58885d79 Iustin Pop
  exceptions are propagated to the caller.
846 58885d79 Iustin Pop

847 58885d79 Iustin Pop
  @type fn: callable
848 58885d79 Iustin Pop
  @param fn: function to apply to the value
849 58885d79 Iustin Pop
  @param val: the value to be converted
850 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
851 58885d79 Iustin Pop
      otherwise the original value.
852 a8083063 Iustin Pop

853 a8083063 Iustin Pop
  """
854 a8083063 Iustin Pop
  try:
855 a8083063 Iustin Pop
    nv = fn(val)
856 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
857 a8083063 Iustin Pop
    nv = val
858 a8083063 Iustin Pop
  return nv
859 a8083063 Iustin Pop
860 a8083063 Iustin Pop
861 a8083063 Iustin Pop
def IsValidIP(ip):
862 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
863 a8083063 Iustin Pop

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

867 58885d79 Iustin Pop
  @type ip: str
868 58885d79 Iustin Pop
  @param ip: the address to be checked
869 58885d79 Iustin Pop
  @rtype: a regular expression match object
870 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
871 58885d79 Iustin Pop
      address is not valid
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
  """
874 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
875 58885d79 Iustin Pop
  #TODO: convert and return only boolean
876 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
877 a8083063 Iustin Pop
878 a8083063 Iustin Pop
879 a8083063 Iustin Pop
def IsValidShellParam(word):
880 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
881 a8083063 Iustin Pop

882 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
883 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
884 a8083063 Iustin Pop
  the actual command.
885 a8083063 Iustin Pop

886 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
887 a8083063 Iustin Pop
  side.
888 a8083063 Iustin Pop

889 58885d79 Iustin Pop
  @type word: str
890 58885d79 Iustin Pop
  @param word: the word to check
891 58885d79 Iustin Pop
  @rtype: boolean
892 58885d79 Iustin Pop
  @return: True if the word is 'safe'
893 58885d79 Iustin Pop

894 a8083063 Iustin Pop
  """
895 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
896 a8083063 Iustin Pop
897 a8083063 Iustin Pop
898 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
899 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
900 a8083063 Iustin Pop

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

906 58885d79 Iustin Pop
  @type template: str
907 58885d79 Iustin Pop
  @param template: the string holding the template for the
908 58885d79 Iustin Pop
      string formatting
909 58885d79 Iustin Pop
  @rtype: str
910 58885d79 Iustin Pop
  @return: the expanded command line
911 58885d79 Iustin Pop

912 a8083063 Iustin Pop
  """
913 a8083063 Iustin Pop
  for word in args:
914 a8083063 Iustin Pop
    if not IsValidShellParam(word):
915 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
916 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
917 a8083063 Iustin Pop
  return template % args
918 a8083063 Iustin Pop
919 a8083063 Iustin Pop
920 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
921 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
922 a8083063 Iustin Pop

923 58885d79 Iustin Pop
  @type value: int
924 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
925 9fbfbb7b Iustin Pop
  @type units: char
926 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
927 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
928 9fbfbb7b Iustin Pop
      - 'm' for MiBs
929 9fbfbb7b Iustin Pop
      - 'g' for GiBs
930 9fbfbb7b Iustin Pop
      - 't' for TiBs
931 58885d79 Iustin Pop
  @rtype: str
932 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
933 a8083063 Iustin Pop

934 a8083063 Iustin Pop
  """
935 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
936 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
937 a8083063 Iustin Pop
938 9fbfbb7b Iustin Pop
  suffix = ''
939 9fbfbb7b Iustin Pop
940 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
941 9fbfbb7b Iustin Pop
    if units == 'h':
942 9fbfbb7b Iustin Pop
      suffix = 'M'
943 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
944 9fbfbb7b Iustin Pop
945 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
946 9fbfbb7b Iustin Pop
    if units == 'h':
947 9fbfbb7b Iustin Pop
      suffix = 'G'
948 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
949 a8083063 Iustin Pop
950 a8083063 Iustin Pop
  else:
951 9fbfbb7b Iustin Pop
    if units == 'h':
952 9fbfbb7b Iustin Pop
      suffix = 'T'
953 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
954 a8083063 Iustin Pop
955 a8083063 Iustin Pop
956 a8083063 Iustin Pop
def ParseUnit(input_string):
957 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
958 a8083063 Iustin Pop

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

963 a8083063 Iustin Pop
  """
964 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
965 a8083063 Iustin Pop
  if not m:
966 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
967 a8083063 Iustin Pop
968 a8083063 Iustin Pop
  value = float(m.groups()[0])
969 a8083063 Iustin Pop
970 a8083063 Iustin Pop
  unit = m.groups()[1]
971 a8083063 Iustin Pop
  if unit:
972 a8083063 Iustin Pop
    lcunit = unit.lower()
973 a8083063 Iustin Pop
  else:
974 a8083063 Iustin Pop
    lcunit = 'm'
975 a8083063 Iustin Pop
976 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
977 a8083063 Iustin Pop
    # Value already in MiB
978 a8083063 Iustin Pop
    pass
979 a8083063 Iustin Pop
980 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
981 a8083063 Iustin Pop
    value *= 1024
982 a8083063 Iustin Pop
983 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
984 a8083063 Iustin Pop
    value *= 1024 * 1024
985 a8083063 Iustin Pop
986 a8083063 Iustin Pop
  else:
987 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
988 a8083063 Iustin Pop
989 a8083063 Iustin Pop
  # Make sure we round up
990 a8083063 Iustin Pop
  if int(value) < value:
991 a8083063 Iustin Pop
    value += 1
992 a8083063 Iustin Pop
993 a8083063 Iustin Pop
  # Round up to the next multiple of 4
994 a8083063 Iustin Pop
  value = int(value)
995 a8083063 Iustin Pop
  if value % 4:
996 a8083063 Iustin Pop
    value += 4 - value % 4
997 a8083063 Iustin Pop
998 a8083063 Iustin Pop
  return value
999 a8083063 Iustin Pop
1000 a8083063 Iustin Pop
1001 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
1002 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
1003 a8083063 Iustin Pop

1004 58885d79 Iustin Pop
  @type file_name: str
1005 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1006 58885d79 Iustin Pop
  @type key: str
1007 58885d79 Iustin Pop
  @param key: string containing key
1008 58885d79 Iustin Pop

1009 a8083063 Iustin Pop
  """
1010 a8083063 Iustin Pop
  key_fields = key.split()
1011 a8083063 Iustin Pop
1012 a8083063 Iustin Pop
  f = open(file_name, 'a+')
1013 a8083063 Iustin Pop
  try:
1014 a8083063 Iustin Pop
    nl = True
1015 a8083063 Iustin Pop
    for line in f:
1016 a8083063 Iustin Pop
      # Ignore whitespace changes
1017 a8083063 Iustin Pop
      if line.split() == key_fields:
1018 a8083063 Iustin Pop
        break
1019 a8083063 Iustin Pop
      nl = line.endswith('\n')
1020 a8083063 Iustin Pop
    else:
1021 a8083063 Iustin Pop
      if not nl:
1022 a8083063 Iustin Pop
        f.write("\n")
1023 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1024 a8083063 Iustin Pop
      f.write("\n")
1025 a8083063 Iustin Pop
      f.flush()
1026 a8083063 Iustin Pop
  finally:
1027 a8083063 Iustin Pop
    f.close()
1028 a8083063 Iustin Pop
1029 a8083063 Iustin Pop
1030 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1031 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1032 a8083063 Iustin Pop

1033 58885d79 Iustin Pop
  @type file_name: str
1034 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1035 58885d79 Iustin Pop
  @type key: str
1036 58885d79 Iustin Pop
  @param key: string containing key
1037 58885d79 Iustin Pop

1038 a8083063 Iustin Pop
  """
1039 a8083063 Iustin Pop
  key_fields = key.split()
1040 a8083063 Iustin Pop
1041 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1042 a8083063 Iustin Pop
  try:
1043 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1044 a8083063 Iustin Pop
    try:
1045 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1046 59f82e3f Michael Hanselmann
      try:
1047 59f82e3f Michael Hanselmann
        for line in f:
1048 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1049 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1050 59f82e3f Michael Hanselmann
            out.write(line)
1051 899d2a81 Michael Hanselmann
1052 899d2a81 Michael Hanselmann
        out.flush()
1053 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1054 899d2a81 Michael Hanselmann
      finally:
1055 899d2a81 Michael Hanselmann
        f.close()
1056 899d2a81 Michael Hanselmann
    finally:
1057 899d2a81 Michael Hanselmann
      out.close()
1058 899d2a81 Michael Hanselmann
  except:
1059 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1060 899d2a81 Michael Hanselmann
    raise
1061 899d2a81 Michael Hanselmann
1062 899d2a81 Michael Hanselmann
1063 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1064 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1065 899d2a81 Michael Hanselmann

1066 58885d79 Iustin Pop
  @type file_name: str
1067 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1068 58885d79 Iustin Pop
  @type ip: str
1069 58885d79 Iustin Pop
  @param ip: the IP address
1070 58885d79 Iustin Pop
  @type hostname: str
1071 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1072 58885d79 Iustin Pop
  @type aliases: list
1073 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1074 58885d79 Iustin Pop

1075 899d2a81 Michael Hanselmann
  """
1076 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1077 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1078 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1079 7fbb1f65 Michael Hanselmann
1080 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1081 899d2a81 Michael Hanselmann
  try:
1082 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1083 9440aeab Michael Hanselmann
    try:
1084 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1085 9440aeab Michael Hanselmann
      try:
1086 9440aeab Michael Hanselmann
        for line in f:
1087 9440aeab Michael Hanselmann
          fields = line.split()
1088 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1089 9440aeab Michael Hanselmann
            continue
1090 9440aeab Michael Hanselmann
          out.write(line)
1091 9440aeab Michael Hanselmann
1092 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1093 9440aeab Michael Hanselmann
        if aliases:
1094 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1095 9440aeab Michael Hanselmann
        out.write('\n')
1096 9440aeab Michael Hanselmann
1097 9440aeab Michael Hanselmann
        out.flush()
1098 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1099 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1100 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1101 9440aeab Michael Hanselmann
      finally:
1102 9440aeab Michael Hanselmann
        f.close()
1103 9440aeab Michael Hanselmann
    finally:
1104 9440aeab Michael Hanselmann
      out.close()
1105 9440aeab Michael Hanselmann
  except:
1106 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1107 9440aeab Michael Hanselmann
    raise
1108 899d2a81 Michael Hanselmann
1109 899d2a81 Michael Hanselmann
1110 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1111 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1112 d9c02ca6 Michael Hanselmann

1113 58885d79 Iustin Pop
  @type hostname: str
1114 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1115 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1116 58885d79 Iustin Pop

1117 d9c02ca6 Michael Hanselmann
  """
1118 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1119 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1120 d9c02ca6 Michael Hanselmann
1121 d9c02ca6 Michael Hanselmann
1122 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1123 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1124 899d2a81 Michael Hanselmann

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

1127 58885d79 Iustin Pop
  @type file_name: str
1128 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1129 58885d79 Iustin Pop
  @type hostname: str
1130 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1131 58885d79 Iustin Pop

1132 899d2a81 Michael Hanselmann
  """
1133 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1134 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1135 899d2a81 Michael Hanselmann
  try:
1136 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
1137 899d2a81 Michael Hanselmann
    try:
1138 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
1139 899d2a81 Michael Hanselmann
      try:
1140 899d2a81 Michael Hanselmann
        for line in f:
1141 899d2a81 Michael Hanselmann
          fields = line.split()
1142 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
1143 899d2a81 Michael Hanselmann
            names = fields[1:]
1144 899d2a81 Michael Hanselmann
            if hostname in names:
1145 899d2a81 Michael Hanselmann
              while hostname in names:
1146 899d2a81 Michael Hanselmann
                names.remove(hostname)
1147 899d2a81 Michael Hanselmann
              if names:
1148 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1149 899d2a81 Michael Hanselmann
              continue
1150 899d2a81 Michael Hanselmann
1151 899d2a81 Michael Hanselmann
          out.write(line)
1152 59f82e3f Michael Hanselmann
1153 59f82e3f Michael Hanselmann
        out.flush()
1154 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1155 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1156 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1157 59f82e3f Michael Hanselmann
      finally:
1158 59f82e3f Michael Hanselmann
        f.close()
1159 a8083063 Iustin Pop
    finally:
1160 59f82e3f Michael Hanselmann
      out.close()
1161 59f82e3f Michael Hanselmann
  except:
1162 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1163 59f82e3f Michael Hanselmann
    raise
1164 a8083063 Iustin Pop
1165 a8083063 Iustin Pop
1166 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1167 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1168 d9c02ca6 Michael Hanselmann

1169 58885d79 Iustin Pop
  @type hostname: str
1170 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1171 58885d79 Iustin Pop
      full and shot name will be removed from
1172 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1173 58885d79 Iustin Pop

1174 d9c02ca6 Michael Hanselmann
  """
1175 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1176 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1177 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1178 d9c02ca6 Michael Hanselmann
1179 d9c02ca6 Michael Hanselmann
1180 1d466a4f Michael Hanselmann
def TimestampForFilename():
1181 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1182 1d466a4f Michael Hanselmann

1183 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1184 1d466a4f Michael Hanselmann
  separators.
1185 1d466a4f Michael Hanselmann

1186 1d466a4f Michael Hanselmann
  """
1187 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1188 1d466a4f Michael Hanselmann
1189 1d466a4f Michael Hanselmann
1190 a8083063 Iustin Pop
def CreateBackup(file_name):
1191 a8083063 Iustin Pop
  """Creates a backup of a file.
1192 a8083063 Iustin Pop

1193 58885d79 Iustin Pop
  @type file_name: str
1194 58885d79 Iustin Pop
  @param file_name: file to be backed up
1195 58885d79 Iustin Pop
  @rtype: str
1196 58885d79 Iustin Pop
  @return: the path to the newly created backup
1197 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1198 a8083063 Iustin Pop

1199 a8083063 Iustin Pop
  """
1200 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1201 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1202 3ecf6786 Iustin Pop
                                file_name)
1203 a8083063 Iustin Pop
1204 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1205 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1206 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1207 081b1e69 Michael Hanselmann
1208 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1209 081b1e69 Michael Hanselmann
  try:
1210 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1211 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1212 081b1e69 Michael Hanselmann
    try:
1213 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1214 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1215 081b1e69 Michael Hanselmann
    finally:
1216 081b1e69 Michael Hanselmann
      fdst.close()
1217 081b1e69 Michael Hanselmann
  finally:
1218 081b1e69 Michael Hanselmann
    fsrc.close()
1219 081b1e69 Michael Hanselmann
1220 a8083063 Iustin Pop
  return backup_name
1221 a8083063 Iustin Pop
1222 a8083063 Iustin Pop
1223 a8083063 Iustin Pop
def ShellQuote(value):
1224 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1225 3ecf6786 Iustin Pop

1226 58885d79 Iustin Pop
  @type value: str
1227 58885d79 Iustin Pop
  @param value: the argument to be quoted
1228 58885d79 Iustin Pop
  @rtype: str
1229 58885d79 Iustin Pop
  @return: the quoted value
1230 58885d79 Iustin Pop

1231 a8083063 Iustin Pop
  """
1232 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1233 a8083063 Iustin Pop
    return value
1234 a8083063 Iustin Pop
  else:
1235 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1236 a8083063 Iustin Pop
1237 a8083063 Iustin Pop
1238 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1239 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1240 58885d79 Iustin Pop

1241 58885d79 Iustin Pop
  @type args: list
1242 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1243 58885d79 Iustin Pop
  @rtype: str
1244 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1245 a8083063 Iustin Pop

1246 a8083063 Iustin Pop
  """
1247 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1248 88d14415 Michael Hanselmann
1249 88d14415 Michael Hanselmann
1250 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1251 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1252 2c30e9d7 Alexander Schreiber

1253 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1254 58885d79 Iustin Pop
  to it.
1255 58885d79 Iustin Pop

1256 58885d79 Iustin Pop
  @type target: str
1257 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1258 58885d79 Iustin Pop
  @type port: int
1259 58885d79 Iustin Pop
  @param port: the port to connect to
1260 58885d79 Iustin Pop
  @type timeout: int
1261 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1262 58885d79 Iustin Pop
  @type live_port_needed: boolean
1263 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1264 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1265 58885d79 Iustin Pop
  @type source: str or None
1266 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1267 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1268 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1269 2c30e9d7 Alexander Schreiber

1270 2c30e9d7 Alexander Schreiber
  """
1271 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1272 2c30e9d7 Alexander Schreiber
1273 0b5ad33e Iustin Pop
  success = False
1274 2c30e9d7 Alexander Schreiber
1275 b15d625f Iustin Pop
  if source is not None:
1276 b15d625f Iustin Pop
    try:
1277 b15d625f Iustin Pop
      sock.bind((source, 0))
1278 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1279 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1280 b15d625f Iustin Pop
        success = False
1281 2c30e9d7 Alexander Schreiber
1282 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1283 2c30e9d7 Alexander Schreiber
1284 2c30e9d7 Alexander Schreiber
  try:
1285 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1286 2c30e9d7 Alexander Schreiber
    sock.close()
1287 2c30e9d7 Alexander Schreiber
    success = True
1288 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1289 2c30e9d7 Alexander Schreiber
    success = False
1290 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1291 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1292 2c30e9d7 Alexander Schreiber
1293 2c30e9d7 Alexander Schreiber
  return success
1294 eedbda4b Michael Hanselmann
1295 eedbda4b Michael Hanselmann
1296 caad16e2 Iustin Pop
def OwnIpAddress(address):
1297 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1298 caad16e2 Iustin Pop

1299 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1300 caad16e2 Iustin Pop
  address.
1301 caad16e2 Iustin Pop

1302 caad16e2 Iustin Pop
  @type address: string
1303 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1304 caad16e2 Iustin Pop
  @rtype: bool
1305 58885d79 Iustin Pop
  @return: True if we own the address
1306 caad16e2 Iustin Pop

1307 caad16e2 Iustin Pop
  """
1308 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1309 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1310 caad16e2 Iustin Pop
1311 caad16e2 Iustin Pop
1312 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1313 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1314 58885d79 Iustin Pop

1315 58885d79 Iustin Pop
  @type path: str
1316 58885d79 Iustin Pop
  @param path: the directory to enumerate
1317 58885d79 Iustin Pop
  @rtype: list
1318 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1319 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1320 eedbda4b Michael Hanselmann

1321 eedbda4b Michael Hanselmann
  """
1322 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1323 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1324 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1325 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1326 f3299a07 Michael Hanselmann
  files.sort()
1327 f3299a07 Michael Hanselmann
  return files
1328 2f8b60b3 Iustin Pop
1329 2f8b60b3 Iustin Pop
1330 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1331 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1332 257f4c0a Iustin Pop

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

1337 2f8b60b3 Iustin Pop
  """
1338 2f8b60b3 Iustin Pop
  try:
1339 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1340 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1341 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1342 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1343 257f4c0a Iustin Pop
    else:
1344 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1345 257f4c0a Iustin Pop
                                   type(user))
1346 2f8b60b3 Iustin Pop
  except KeyError:
1347 2f8b60b3 Iustin Pop
    return default
1348 2f8b60b3 Iustin Pop
  return result.pw_dir
1349 59072e7e Michael Hanselmann
1350 59072e7e Michael Hanselmann
1351 24818e8f Michael Hanselmann
def NewUUID():
1352 59072e7e Michael Hanselmann
  """Returns a random UUID.
1353 59072e7e Michael Hanselmann

1354 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1355 58885d79 Iustin Pop
      filesystem.
1356 58885d79 Iustin Pop
  @rtype: str
1357 58885d79 Iustin Pop

1358 59072e7e Michael Hanselmann
  """
1359 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1360 087b34fe Iustin Pop
1361 087b34fe Iustin Pop
1362 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1363 33081d90 Iustin Pop
  """Generates a random secret.
1364 33081d90 Iustin Pop

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

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

1373 33081d90 Iustin Pop
  """
1374 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1375 33081d90 Iustin Pop
1376 33081d90 Iustin Pop
1377 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1378 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1379 9dae41ad Guido Trotter

1380 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1381 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1382 9dae41ad Guido Trotter

1383 9dae41ad Guido Trotter
  """
1384 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1385 9dae41ad Guido Trotter
    try:
1386 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1387 9dae41ad Guido Trotter
    except EnvironmentError, err:
1388 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1389 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1390 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1391 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1392 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1393 9dae41ad Guido Trotter
1394 9dae41ad Guido Trotter
1395 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1396 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1397 ca0aa6d0 Michael Hanselmann

1398 016308cb Iustin Pop
  @type size: int
1399 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1400 58885d79 Iustin Pop
  @rtype: str
1401 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1402 ca0aa6d0 Michael Hanselmann

1403 ca0aa6d0 Michael Hanselmann
  """
1404 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1405 ca0aa6d0 Michael Hanselmann
  try:
1406 582ed043 Guido Trotter
    return f.read(size)
1407 ca0aa6d0 Michael Hanselmann
  finally:
1408 ca0aa6d0 Michael Hanselmann
    f.close()
1409 ca0aa6d0 Michael Hanselmann
1410 ca0aa6d0 Michael Hanselmann
1411 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1412 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1413 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1414 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1415 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1416 087b34fe Iustin Pop
  """(Over)write a file atomically.
1417 087b34fe Iustin Pop

1418 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1419 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1420 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1421 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1422 087b34fe Iustin Pop
  mtime/atime of the file.
1423 087b34fe Iustin Pop

1424 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1425 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1426 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1427 087b34fe Iustin Pop
  temporary file should be removed.
1428 087b34fe Iustin Pop

1429 58885d79 Iustin Pop
  @type file_name: str
1430 58885d79 Iustin Pop
  @param file_name: the target filename
1431 58885d79 Iustin Pop
  @type fn: callable
1432 58885d79 Iustin Pop
  @param fn: content writing function, called with
1433 58885d79 Iustin Pop
      file descriptor as parameter
1434 69efe319 Michael Hanselmann
  @type data: str
1435 58885d79 Iustin Pop
  @param data: contents of the file
1436 58885d79 Iustin Pop
  @type mode: int
1437 58885d79 Iustin Pop
  @param mode: file mode
1438 58885d79 Iustin Pop
  @type uid: int
1439 58885d79 Iustin Pop
  @param uid: the owner of the file
1440 58885d79 Iustin Pop
  @type gid: int
1441 58885d79 Iustin Pop
  @param gid: the group of the file
1442 58885d79 Iustin Pop
  @type atime: int
1443 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1444 58885d79 Iustin Pop
  @type mtime: int
1445 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1446 58885d79 Iustin Pop
  @type close: boolean
1447 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1448 58885d79 Iustin Pop
  @type prewrite: callable
1449 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1450 58885d79 Iustin Pop
  @type postwrite: callable
1451 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1452 58885d79 Iustin Pop

1453 58885d79 Iustin Pop
  @rtype: None or int
1454 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1455 58885d79 Iustin Pop
      otherwise the file descriptor
1456 58885d79 Iustin Pop

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

1459 087b34fe Iustin Pop
  """
1460 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1461 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1462 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1463 087b34fe Iustin Pop
1464 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1465 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1466 087b34fe Iustin Pop
1467 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1468 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1469 087b34fe Iustin Pop
                                 " set or None")
1470 087b34fe Iustin Pop
1471 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1472 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1473 087b34fe Iustin Pop
1474 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1475 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1476 81b7354c Iustin Pop
  do_remove = True
1477 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1478 087b34fe Iustin Pop
  # leaves it in place
1479 087b34fe Iustin Pop
  try:
1480 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1481 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1482 087b34fe Iustin Pop
    if mode:
1483 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1484 71714516 Michael Hanselmann
    if callable(prewrite):
1485 71714516 Michael Hanselmann
      prewrite(fd)
1486 087b34fe Iustin Pop
    if data is not None:
1487 087b34fe Iustin Pop
      os.write(fd, data)
1488 087b34fe Iustin Pop
    else:
1489 087b34fe Iustin Pop
      fn(fd)
1490 71714516 Michael Hanselmann
    if callable(postwrite):
1491 71714516 Michael Hanselmann
      postwrite(fd)
1492 087b34fe Iustin Pop
    os.fsync(fd)
1493 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1494 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1495 70f4497c Michael Hanselmann
    if not dry_run:
1496 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1497 81b7354c Iustin Pop
      do_remove = False
1498 087b34fe Iustin Pop
  finally:
1499 71714516 Michael Hanselmann
    if close:
1500 71714516 Michael Hanselmann
      os.close(fd)
1501 71714516 Michael Hanselmann
      result = None
1502 71714516 Michael Hanselmann
    else:
1503 71714516 Michael Hanselmann
      result = fd
1504 81b7354c Iustin Pop
    if do_remove:
1505 81b7354c Iustin Pop
      RemoveFile(new_name)
1506 78feb6fb Guido Trotter
1507 71714516 Michael Hanselmann
  return result
1508 71714516 Michael Hanselmann
1509 78feb6fb Guido Trotter
1510 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
1511 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
1512 e587b46a Guido Trotter

1513 e587b46a Guido Trotter
  @type strict: boolean
1514 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1515 e587b46a Guido Trotter
      non-empty line
1516 e587b46a Guido Trotter

1517 e587b46a Guido Trotter
  """
1518 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1519 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1520 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1521 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1522 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1523 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1524 e587b46a Guido Trotter
                              file_name)
1525 e587b46a Guido Trotter
  return full_lines[0]
1526 e587b46a Guido Trotter
1527 e587b46a Guido Trotter
1528 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1529 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1530 7b4126b7 Iustin Pop

1531 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1532 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1533 7b4126b7 Iustin Pop
  value, the index will be returned.
1534 7b4126b7 Iustin Pop

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

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

1540 58885d79 Iustin Pop
  @type seq: sequence
1541 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1542 58885d79 Iustin Pop
  @type base: int
1543 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1544 58885d79 Iustin Pop
  @rtype: int
1545 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1546 7b4126b7 Iustin Pop

1547 7b4126b7 Iustin Pop
  """
1548 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1549 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1550 7b4126b7 Iustin Pop
    if elem > idx + base:
1551 7b4126b7 Iustin Pop
      # idx is not used
1552 7b4126b7 Iustin Pop
      return idx + base
1553 7b4126b7 Iustin Pop
  return None
1554 7b4126b7 Iustin Pop
1555 7b4126b7 Iustin Pop
1556 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1557 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1558 dcd511c8 Guido Trotter

1559 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1560 dfdc4060 Guido Trotter

1561 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1562 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1563 dfdc4060 Guido Trotter
  @type event: integer
1564 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1565 dcd511c8 Guido Trotter
  @type timeout: float or None
1566 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1567 dcd511c8 Guido Trotter
  @rtype: int or None
1568 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1569 dcd511c8 Guido Trotter

1570 dcd511c8 Guido Trotter
  """
1571 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1572 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1573 dcd511c8 Guido Trotter
1574 dcd511c8 Guido Trotter
  if timeout is not None:
1575 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1576 dcd511c8 Guido Trotter
    timeout *= 1000
1577 dcd511c8 Guido Trotter
1578 dcd511c8 Guido Trotter
  poller = select.poll()
1579 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1580 dcd511c8 Guido Trotter
  try:
1581 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1582 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1583 dfdc4060 Guido Trotter
    # every so often.
1584 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1585 dfdc4060 Guido Trotter
  except select.error, err:
1586 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1587 dfdc4060 Guido Trotter
      raise
1588 dfdc4060 Guido Trotter
    io_events = []
1589 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1590 dfdc4060 Guido Trotter
    return io_events[0][1]
1591 dfdc4060 Guido Trotter
  else:
1592 dfdc4060 Guido Trotter
    return None
1593 dfdc4060 Guido Trotter
1594 dfdc4060 Guido Trotter
1595 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1596 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1597 dfdc4060 Guido Trotter

1598 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1599 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1600 dfdc4060 Guido Trotter
  expired.
1601 dfdc4060 Guido Trotter

1602 dfdc4060 Guido Trotter
  """
1603 dfdc4060 Guido Trotter
1604 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1605 dfdc4060 Guido Trotter
    self.timeout = timeout
1606 dfdc4060 Guido Trotter
1607 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1608 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1609 dfdc4060 Guido Trotter
    if result is None:
1610 dfdc4060 Guido Trotter
      raise RetryAgain()
1611 dfdc4060 Guido Trotter
    else:
1612 dfdc4060 Guido Trotter
      return result
1613 dfdc4060 Guido Trotter
1614 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1615 dfdc4060 Guido Trotter
    self.timeout = timeout
1616 dfdc4060 Guido Trotter
1617 dfdc4060 Guido Trotter
1618 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1619 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1620 dfdc4060 Guido Trotter

1621 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1622 dfdc4060 Guido Trotter

1623 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1624 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1625 dfdc4060 Guido Trotter
  @type event: integer
1626 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1627 dfdc4060 Guido Trotter
  @type timeout: float or None
1628 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1629 dfdc4060 Guido Trotter
  @rtype: int or None
1630 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1631 dfdc4060 Guido Trotter

1632 dfdc4060 Guido Trotter
  """
1633 dfdc4060 Guido Trotter
  if timeout is not None:
1634 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1635 1b429e2a Iustin Pop
    try:
1636 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1637 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1638 1b429e2a Iustin Pop
    except RetryTimeout:
1639 1b429e2a Iustin Pop
      result = None
1640 dfdc4060 Guido Trotter
  else:
1641 dfdc4060 Guido Trotter
    result = None
1642 dfdc4060 Guido Trotter
    while result is None:
1643 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1644 dfdc4060 Guido Trotter
  return result
1645 dcd511c8 Guido Trotter
1646 dcd511c8 Guido Trotter
1647 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1648 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1649 f7414041 Michael Hanselmann

1650 f7414041 Michael Hanselmann
  Element order is preserved.
1651 58885d79 Iustin Pop

1652 58885d79 Iustin Pop
  @type seq: sequence
1653 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1654 58885d79 Iustin Pop
  @rtype: list
1655 58885d79 Iustin Pop
  @return: list of unique elements from seq
1656 58885d79 Iustin Pop

1657 f7414041 Michael Hanselmann
  """
1658 f7414041 Michael Hanselmann
  seen = set()
1659 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1660 1862d460 Alexander Schreiber
1661 1862d460 Alexander Schreiber
1662 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1663 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1664 1862d460 Alexander Schreiber

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

1668 58885d79 Iustin Pop
  @type mac: str
1669 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1670 82187135 Renรฉ Nussbaumer
  @rtype: str
1671 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1672 82187135 Renรฉ Nussbaumer

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

1675 1862d460 Alexander Schreiber
  """
1676 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1677 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1678 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1679 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1680 82187135 Renรฉ Nussbaumer
1681 82187135 Renรฉ Nussbaumer
  return mac.lower()
1682 06009e27 Iustin Pop
1683 06009e27 Iustin Pop
1684 06009e27 Iustin Pop
def TestDelay(duration):
1685 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1686 06009e27 Iustin Pop

1687 58885d79 Iustin Pop
  @type duration: float
1688 58885d79 Iustin Pop
  @param duration: the sleep duration
1689 58885d79 Iustin Pop
  @rtype: boolean
1690 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1691 58885d79 Iustin Pop

1692 06009e27 Iustin Pop
  """
1693 06009e27 Iustin Pop
  if duration < 0:
1694 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1695 06009e27 Iustin Pop
  time.sleep(duration)
1696 38ea42a1 Iustin Pop
  return True, None
1697 8f765069 Iustin Pop
1698 8f765069 Iustin Pop
1699 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1700 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1701 8f765069 Iustin Pop

1702 7d88772a Iustin Pop
  @type fd: int
1703 7d88772a Iustin Pop
  @param fd: the file descriptor
1704 7d88772a Iustin Pop
  @type retries: int
1705 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1706 7d88772a Iustin Pop
      other error than EBADF
1707 7d88772a Iustin Pop

1708 7d88772a Iustin Pop
  """
1709 7d88772a Iustin Pop
  try:
1710 7d88772a Iustin Pop
    os.close(fd)
1711 7d88772a Iustin Pop
  except OSError, err:
1712 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1713 7d88772a Iustin Pop
      if retries > 0:
1714 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1715 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1716 7d88772a Iustin Pop
    # ignore this and go on
1717 7d88772a Iustin Pop
1718 7d88772a Iustin Pop
1719 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1720 7d88772a Iustin Pop
  """Close file descriptors.
1721 7d88772a Iustin Pop

1722 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1723 7d88772a Iustin Pop
  stdin/out/err).
1724 8f765069 Iustin Pop

1725 58885d79 Iustin Pop
  @type noclose_fds: list or None
1726 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1727 58885d79 Iustin Pop
      that should not be closed
1728 58885d79 Iustin Pop

1729 8f765069 Iustin Pop
  """
1730 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1731 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1732 8f765069 Iustin Pop
    try:
1733 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1734 8f765069 Iustin Pop
      if MAXFD < 0:
1735 8f765069 Iustin Pop
        MAXFD = 1024
1736 8f765069 Iustin Pop
    except OSError:
1737 8f765069 Iustin Pop
      MAXFD = 1024
1738 8f765069 Iustin Pop
  else:
1739 8f765069 Iustin Pop
    MAXFD = 1024
1740 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1741 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1742 7d88772a Iustin Pop
    maxfd = MAXFD
1743 7d88772a Iustin Pop
1744 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1745 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1746 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1747 7d88772a Iustin Pop
      continue
1748 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1749 7d88772a Iustin Pop
1750 7d88772a Iustin Pop
1751 4b6fa0bf Luca Bigliardi
def Mlockall():
1752 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
1753 4b6fa0bf Luca Bigliardi

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

1757 4b6fa0bf Luca Bigliardi
  """
1758 4b6fa0bf Luca Bigliardi
  if ctypes is None:
1759 4b6fa0bf Luca Bigliardi
    logging.warning("Cannot set memory lock, ctypes module not found")
1760 4b6fa0bf Luca Bigliardi
    return
1761 4b6fa0bf Luca Bigliardi
1762 4b6fa0bf Luca Bigliardi
  libc = ctypes.cdll.LoadLibrary("libc.so.6")
1763 4b6fa0bf Luca Bigliardi
  if libc is None:
1764 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
1765 4b6fa0bf Luca Bigliardi
    return
1766 4b6fa0bf Luca Bigliardi
1767 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
1768 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
1769 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
1770 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
1771 4b6fa0bf Luca Bigliardi
  # the actual error code was.
1772 4b6fa0bf Luca Bigliardi
  libc.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
1773 4b6fa0bf Luca Bigliardi
1774 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
1775 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock: %s" %
1776 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
1777 4b6fa0bf Luca Bigliardi
    return
1778 4b6fa0bf Luca Bigliardi
1779 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
1780 4b6fa0bf Luca Bigliardi
1781 4b6fa0bf Luca Bigliardi
1782 7d88772a Iustin Pop
def Daemonize(logfile):
1783 7d88772a Iustin Pop
  """Daemonize the current process.
1784 7d88772a Iustin Pop

1785 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1786 7d88772a Iustin Pop
  runs it in the background as a daemon.
1787 7d88772a Iustin Pop

1788 7d88772a Iustin Pop
  @type logfile: str
1789 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1790 7d88772a Iustin Pop
  @rtype: int
1791 5fcc718f Iustin Pop
  @return: the value zero
1792 7d88772a Iustin Pop

1793 7d88772a Iustin Pop
  """
1794 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1795 7260cfbe Iustin Pop
  # yes, we really want os._exit
1796 7d88772a Iustin Pop
  UMASK = 077
1797 7d88772a Iustin Pop
  WORKDIR = "/"
1798 8f765069 Iustin Pop
1799 8f765069 Iustin Pop
  # this might fail
1800 8f765069 Iustin Pop
  pid = os.fork()
1801 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1802 8f765069 Iustin Pop
    os.setsid()
1803 8f765069 Iustin Pop
    # this might fail
1804 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1805 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1806 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1807 8f765069 Iustin Pop
      os.umask(UMASK)
1808 8f765069 Iustin Pop
    else:
1809 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1810 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1811 8f765069 Iustin Pop
  else:
1812 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1813 8f765069 Iustin Pop
1814 7d88772a Iustin Pop
  for fd in range(3):
1815 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1816 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1817 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1818 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1819 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1820 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1821 7d88772a Iustin Pop
  os.dup2(1, 2)
1822 8f765069 Iustin Pop
  return 0
1823 57c177af Iustin Pop
1824 57c177af Iustin Pop
1825 53beffbb Iustin Pop
def DaemonPidFileName(name):
1826 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1827 58885d79 Iustin Pop

1828 58885d79 Iustin Pop
  @type name: str
1829 58885d79 Iustin Pop
  @param name: the daemon name
1830 58885d79 Iustin Pop
  @rtype: str
1831 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1832 58885d79 Iustin Pop
      daemon name
1833 b330ac0b Guido Trotter

1834 b330ac0b Guido Trotter
  """
1835 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1836 b330ac0b Guido Trotter
1837 b330ac0b Guido Trotter
1838 2826b361 Guido Trotter
def EnsureDaemon(name):
1839 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1840 2826b361 Guido Trotter

1841 2826b361 Guido Trotter
  """
1842 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1843 2826b361 Guido Trotter
  if result.failed:
1844 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1845 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1846 2826b361 Guido Trotter
    return False
1847 2826b361 Guido Trotter
1848 2826b361 Guido Trotter
  return True
1849 2826b361 Guido Trotter
1850 2826b361 Guido Trotter
1851 b330ac0b Guido Trotter
def WritePidFile(name):
1852 b330ac0b Guido Trotter
  """Write the current process pidfile.
1853 b330ac0b Guido Trotter

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

1856 58885d79 Iustin Pop
  @type name: str
1857 58885d79 Iustin Pop
  @param name: the daemon name to use
1858 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1859 58885d79 Iustin Pop
      points to a live process
1860 b330ac0b Guido Trotter

1861 b330ac0b Guido Trotter
  """
1862 b330ac0b Guido Trotter
  pid = os.getpid()
1863 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1864 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1865 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1866 b330ac0b Guido Trotter
1867 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1868 b330ac0b Guido Trotter
1869 b330ac0b Guido Trotter
1870 b330ac0b Guido Trotter
def RemovePidFile(name):
1871 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1872 b330ac0b Guido Trotter

1873 b330ac0b Guido Trotter
  Any errors are ignored.
1874 b330ac0b Guido Trotter

1875 58885d79 Iustin Pop
  @type name: str
1876 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1877 58885d79 Iustin Pop

1878 b330ac0b Guido Trotter
  """
1879 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1880 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1881 b330ac0b Guido Trotter
  try:
1882 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1883 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1884 b330ac0b Guido Trotter
    pass
1885 b330ac0b Guido Trotter
1886 b330ac0b Guido Trotter
1887 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1888 ff5251bc Iustin Pop
                waitpid=False):
1889 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1890 b2a1f511 Iustin Pop

1891 b2a1f511 Iustin Pop
  @type pid: int
1892 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1893 38206f3c Iustin Pop
  @type signal_: int
1894 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1895 b2a1f511 Iustin Pop
  @type timeout: int
1896 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1897 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1898 b2a1f511 Iustin Pop
                  will be done
1899 ff5251bc Iustin Pop
  @type waitpid: boolean
1900 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1901 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1902 ff5251bc Iustin Pop
      would remain as zombie
1903 b2a1f511 Iustin Pop

1904 b2a1f511 Iustin Pop
  """
1905 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1906 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1907 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1908 ff5251bc Iustin Pop
    if wait:
1909 ff5251bc Iustin Pop
      try:
1910 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1911 ff5251bc Iustin Pop
      except OSError:
1912 ff5251bc Iustin Pop
        pass
1913 ff5251bc Iustin Pop
1914 b2a1f511 Iustin Pop
  if pid <= 0:
1915 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1916 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1917 b2a1f511 Iustin Pop
1918 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1919 b2a1f511 Iustin Pop
    return
1920 31892b4c Michael Hanselmann
1921 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1922 31892b4c Michael Hanselmann
1923 b2a1f511 Iustin Pop
  if timeout <= 0:
1924 b2a1f511 Iustin Pop
    return
1925 7167159a Michael Hanselmann
1926 31892b4c Michael Hanselmann
  def _CheckProcess():
1927 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1928 31892b4c Michael Hanselmann
      return
1929 31892b4c Michael Hanselmann
1930 7167159a Michael Hanselmann
    try:
1931 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1932 7167159a Michael Hanselmann
    except OSError:
1933 31892b4c Michael Hanselmann
      raise RetryAgain()
1934 31892b4c Michael Hanselmann
1935 31892b4c Michael Hanselmann
    if result_pid > 0:
1936 31892b4c Michael Hanselmann
      return
1937 31892b4c Michael Hanselmann
1938 31892b4c Michael Hanselmann
    raise RetryAgain()
1939 31892b4c Michael Hanselmann
1940 31892b4c Michael Hanselmann
  try:
1941 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1942 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1943 31892b4c Michael Hanselmann
  except RetryTimeout:
1944 31892b4c Michael Hanselmann
    pass
1945 7167159a Michael Hanselmann
1946 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1947 7167159a Michael Hanselmann
    # Kill process if it's still alive
1948 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1949 b2a1f511 Iustin Pop
1950 b2a1f511 Iustin Pop
1951 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1952 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1953 57c177af Iustin Pop

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

1957 58885d79 Iustin Pop
  @type name: str
1958 58885d79 Iustin Pop
  @param name: the name to look for
1959 58885d79 Iustin Pop
  @type search_path: str
1960 58885d79 Iustin Pop
  @param search_path: location to start at
1961 58885d79 Iustin Pop
  @type test: callable
1962 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1963 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1964 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1965 58885d79 Iustin Pop
  @rtype: str or None
1966 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1967 57c177af Iustin Pop

1968 57c177af Iustin Pop
  """
1969 f95c81bf Iustin Pop
  # validate the filename mask
1970 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1971 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1972 f95c81bf Iustin Pop
                     name)
1973 f95c81bf Iustin Pop
    return None
1974 f95c81bf Iustin Pop
1975 57c177af Iustin Pop
  for dir_name in search_path:
1976 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1977 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1978 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1979 f95c81bf Iustin Pop
    # basename
1980 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1981 57c177af Iustin Pop
      return item_name
1982 57c177af Iustin Pop
  return None
1983 8d1a2a64 Michael Hanselmann
1984 8d1a2a64 Michael Hanselmann
1985 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1986 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1987 8d1a2a64 Michael Hanselmann

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

1991 58885d79 Iustin Pop
  @type vglist: dict
1992 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1993 58885d79 Iustin Pop
  @type vgname: str
1994 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1995 58885d79 Iustin Pop
  @type minsize: int
1996 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1997 58885d79 Iustin Pop
  @rtype: None or str
1998 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1999 8d1a2a64 Michael Hanselmann

2000 8d1a2a64 Michael Hanselmann
  """
2001 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2002 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2003 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2004 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2005 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2006 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2007 8d1a2a64 Michael Hanselmann
  return None
2008 7996a135 Iustin Pop
2009 7996a135 Iustin Pop
2010 45bc5e4a Michael Hanselmann
def SplitTime(value):
2011 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2012 739be818 Michael Hanselmann

2013 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2014 45bc5e4a Michael Hanselmann
  @type value: int or float
2015 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2016 739be818 Michael Hanselmann

2017 739be818 Michael Hanselmann
  """
2018 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2019 45bc5e4a Michael Hanselmann
2020 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2021 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2022 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2023 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2024 45bc5e4a Michael Hanselmann
2025 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2026 739be818 Michael Hanselmann
2027 739be818 Michael Hanselmann
2028 739be818 Michael Hanselmann
def MergeTime(timetuple):
2029 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2030 739be818 Michael Hanselmann

2031 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2032 739be818 Michael Hanselmann
  @type timetuple: tuple
2033 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2034 739be818 Michael Hanselmann

2035 739be818 Michael Hanselmann
  """
2036 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2037 739be818 Michael Hanselmann
2038 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2039 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2040 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2041 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2042 739be818 Michael Hanselmann
2043 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2044 739be818 Michael Hanselmann
2045 739be818 Michael Hanselmann
2046 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
2047 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
2048 4a8b186a Michael Hanselmann

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

2053 cd50653c Guido Trotter
  @type daemon_name: string
2054 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2055 58885d79 Iustin Pop
  @rtype: int
2056 58885d79 Iustin Pop

2057 4a8b186a Michael Hanselmann
  """
2058 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
2059 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
2060 cd50653c Guido Trotter
2061 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
2062 4a8b186a Michael Hanselmann
  try:
2063 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
2064 4a8b186a Michael Hanselmann
  except socket.error:
2065 cd50653c Guido Trotter
    port = default_port
2066 4a8b186a Michael Hanselmann
2067 4a8b186a Michael Hanselmann
  return port
2068 4a8b186a Michael Hanselmann
2069 4a8b186a Michael Hanselmann
2070 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2071 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
2072 82d9caef Iustin Pop
  """Configures the logging module.
2073 82d9caef Iustin Pop

2074 58885d79 Iustin Pop
  @type logfile: str
2075 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2076 ea34193f Iustin Pop
  @type debug: integer
2077 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2078 58885d79 Iustin Pop
      only those at C{INFO} and above level
2079 58885d79 Iustin Pop
  @type stderr_logging: boolean
2080 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2081 58885d79 Iustin Pop
  @type program: str
2082 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2083 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2084 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2085 551b6283 Iustin Pop
  @type syslog: string
2086 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2087 551b6283 Iustin Pop
      - if no, syslog is not used
2088 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2089 551b6283 Iustin Pop
      - if only, only syslog is used
2090 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2091 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2092 58885d79 Iustin Pop

2093 82d9caef Iustin Pop
  """
2094 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2095 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2096 d21d09d6 Iustin Pop
  if multithreaded:
2097 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2098 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2099 82d9caef Iustin Pop
  if debug:
2100 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2101 551b6283 Iustin Pop
    # no debug info for syslog loggers
2102 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2103 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2104 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2105 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2106 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2107 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2108 82d9caef Iustin Pop
2109 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2110 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2111 82d9caef Iustin Pop
2112 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2113 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2114 7d88772a Iustin Pop
    handler.close()
2115 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2116 6346a9e5 Michael Hanselmann
2117 82d9caef Iustin Pop
  if stderr_logging:
2118 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2119 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2120 82d9caef Iustin Pop
    if debug:
2121 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2122 82d9caef Iustin Pop
    else:
2123 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2124 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2125 82d9caef Iustin Pop
2126 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2127 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2128 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2129 551b6283 Iustin Pop
                                                    facility)
2130 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2131 551b6283 Iustin Pop
    # Never enable debug over syslog
2132 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2133 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2134 551b6283 Iustin Pop
2135 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2136 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2137 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2138 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2139 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2140 551b6283 Iustin Pop
    try:
2141 551b6283 Iustin Pop
      logfile_handler = logging.FileHandler(logfile)
2142 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2143 551b6283 Iustin Pop
      if debug:
2144 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2145 551b6283 Iustin Pop
      else:
2146 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2147 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2148 551b6283 Iustin Pop
    except EnvironmentError:
2149 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2150 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2151 551b6283 Iustin Pop
      else:
2152 551b6283 Iustin Pop
        # we need to re-raise the exception
2153 551b6283 Iustin Pop
        raise
2154 82d9caef Iustin Pop
2155 016d04b3 Michael Hanselmann
2156 da961187 Guido Trotter
def IsNormAbsPath(path):
2157 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2158 da961187 Guido Trotter

2159 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2160 da961187 Guido Trotter

2161 da961187 Guido Trotter
  """
2162 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2163 82d9caef Iustin Pop
2164 016d04b3 Michael Hanselmann
2165 4bb678e9 Iustin Pop
def PathJoin(*args):
2166 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2167 4bb678e9 Iustin Pop

2168 4bb678e9 Iustin Pop
  Requirements:
2169 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2170 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2171 4bb678e9 Iustin Pop
        since we check for normalization at the end
2172 4bb678e9 Iustin Pop

2173 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2174 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2175 4bb678e9 Iustin Pop

2176 4bb678e9 Iustin Pop
  """
2177 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2178 4bb678e9 Iustin Pop
  assert args
2179 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2180 4bb678e9 Iustin Pop
  root = args[0]
2181 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2182 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2183 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2184 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2185 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2186 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2187 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2188 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2189 4bb678e9 Iustin Pop
  if prefix != root:
2190 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2191 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2192 4bb678e9 Iustin Pop
  return result
2193 4bb678e9 Iustin Pop
2194 4bb678e9 Iustin Pop
2195 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2196 f65f63ef Iustin Pop
  """Return the last lines from a file.
2197 f65f63ef Iustin Pop

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

2202 f65f63ef Iustin Pop
  @param fname: the file name
2203 f65f63ef Iustin Pop
  @type lines: int
2204 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2205 f65f63ef Iustin Pop

2206 f65f63ef Iustin Pop
  """
2207 f65f63ef Iustin Pop
  fd = open(fname, "r")
2208 f65f63ef Iustin Pop
  try:
2209 f65f63ef Iustin Pop
    fd.seek(0, 2)
2210 f65f63ef Iustin Pop
    pos = fd.tell()
2211 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2212 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2213 f65f63ef Iustin Pop
    raw_data = fd.read()
2214 f65f63ef Iustin Pop
  finally:
2215 f65f63ef Iustin Pop
    fd.close()
2216 f65f63ef Iustin Pop
2217 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2218 f65f63ef Iustin Pop
  return rows[-lines:]
2219 f65f63ef Iustin Pop
2220 f65f63ef Iustin Pop
2221 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2222 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2223 27e46076 Michael Hanselmann

2224 27e46076 Michael Hanselmann
  @type value: string
2225 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2226 27e46076 Michael Hanselmann

2227 27e46076 Michael Hanselmann
  """
2228 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2229 27e46076 Michael Hanselmann
  if m:
2230 27e46076 Michael Hanselmann
    # We have an offset
2231 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2232 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2233 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2234 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2235 27e46076 Michael Hanselmann
  else:
2236 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2237 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2238 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2239 27e46076 Michael Hanselmann
    utcoffset = 0
2240 27e46076 Michael Hanselmann
2241 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2242 27e46076 Michael Hanselmann
2243 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2244 27e46076 Michael Hanselmann
2245 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2246 27e46076 Michael Hanselmann
2247 27e46076 Michael Hanselmann
2248 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2249 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2250 27e46076 Michael Hanselmann

2251 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2252 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2253 27e46076 Michael Hanselmann

2254 27e46076 Michael Hanselmann
  """
2255 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2256 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2257 27e46076 Michael Hanselmann
  try:
2258 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2259 27e46076 Michael Hanselmann
  except AttributeError:
2260 27e46076 Michael Hanselmann
    not_before = None
2261 27e46076 Michael Hanselmann
  else:
2262 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2263 27e46076 Michael Hanselmann
2264 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2265 27e46076 Michael Hanselmann
      not_before = None
2266 27e46076 Michael Hanselmann
    else:
2267 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2268 27e46076 Michael Hanselmann
2269 27e46076 Michael Hanselmann
  try:
2270 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2271 27e46076 Michael Hanselmann
  except AttributeError:
2272 27e46076 Michael Hanselmann
    not_after = None
2273 27e46076 Michael Hanselmann
  else:
2274 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2275 27e46076 Michael Hanselmann
2276 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2277 27e46076 Michael Hanselmann
      not_after = None
2278 27e46076 Michael Hanselmann
    else:
2279 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2280 27e46076 Michael Hanselmann
2281 27e46076 Michael Hanselmann
  return (not_before, not_after)
2282 27e46076 Michael Hanselmann
2283 27e46076 Michael Hanselmann
2284 26f15862 Iustin Pop
def SafeEncode(text):
2285 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2286 26f15862 Iustin Pop

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

2296 26f15862 Iustin Pop
  @type text: str or unicode
2297 26f15862 Iustin Pop
  @param text: input data
2298 26f15862 Iustin Pop
  @rtype: str
2299 26f15862 Iustin Pop
  @return: a safe version of text
2300 26f15862 Iustin Pop

2301 26f15862 Iustin Pop
  """
2302 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2303 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2304 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2305 d392fa34 Iustin Pop
  resu = ""
2306 d392fa34 Iustin Pop
  for char in text:
2307 d392fa34 Iustin Pop
    c = ord(char)
2308 d392fa34 Iustin Pop
    if char  == '\t':
2309 d392fa34 Iustin Pop
      resu += r'\t'
2310 d392fa34 Iustin Pop
    elif char == '\n':
2311 d392fa34 Iustin Pop
      resu += r'\n'
2312 d392fa34 Iustin Pop
    elif char == '\r':
2313 d392fa34 Iustin Pop
      resu += r'\'r'
2314 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2315 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2316 d392fa34 Iustin Pop
    else:
2317 d392fa34 Iustin Pop
      resu += char
2318 d392fa34 Iustin Pop
  return resu
2319 26f15862 Iustin Pop
2320 26f15862 Iustin Pop
2321 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2322 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2323 5b69bc7c Iustin Pop

2324 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2325 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2326 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2327 5b69bc7c Iustin Pop
  separator):
2328 5b69bc7c Iustin Pop
    - a plain , separates the elements
2329 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2330 5b69bc7c Iustin Pop
      backslash plus a separator comma
2331 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2332 5b69bc7c Iustin Pop
      non-separator comma
2333 5b69bc7c Iustin Pop

2334 5b69bc7c Iustin Pop
  @type text: string
2335 5b69bc7c Iustin Pop
  @param text: the string to split
2336 5b69bc7c Iustin Pop
  @type sep: string
2337 5b69bc7c Iustin Pop
  @param text: the separator
2338 5b69bc7c Iustin Pop
  @rtype: string
2339 5b69bc7c Iustin Pop
  @return: a list of strings
2340 5b69bc7c Iustin Pop

2341 5b69bc7c Iustin Pop
  """
2342 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2343 5b69bc7c Iustin Pop
  slist = text.split(sep)
2344 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2345 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2346 5b69bc7c Iustin Pop
  rlist = []
2347 5b69bc7c Iustin Pop
  while slist:
2348 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2349 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2350 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2351 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2352 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2353 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2354 5b69bc7c Iustin Pop
        # the next step
2355 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2356 5b69bc7c Iustin Pop
        continue
2357 5b69bc7c Iustin Pop
    rlist.append(e1)
2358 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2359 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2360 5b69bc7c Iustin Pop
  return rlist
2361 5b69bc7c Iustin Pop
2362 5b69bc7c Iustin Pop
2363 ab3e6da8 Iustin Pop
def CommaJoin(names):
2364 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2365 ab3e6da8 Iustin Pop

2366 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2367 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2368 ab3e6da8 Iustin Pop

2369 ab3e6da8 Iustin Pop
  """
2370 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2371 ab3e6da8 Iustin Pop
2372 ab3e6da8 Iustin Pop
2373 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2374 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2375 3f6a47a8 Michael Hanselmann

2376 3f6a47a8 Michael Hanselmann
  @type value: int
2377 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2378 3f6a47a8 Michael Hanselmann
  @rtype: int
2379 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2380 3f6a47a8 Michael Hanselmann

2381 3f6a47a8 Michael Hanselmann
  """
2382 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2383 3f6a47a8 Michael Hanselmann
2384 3f6a47a8 Michael Hanselmann
2385 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2386 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2387 3f6a47a8 Michael Hanselmann

2388 3f6a47a8 Michael Hanselmann
  @type path: string
2389 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2390 3f6a47a8 Michael Hanselmann
  @rtype: int
2391 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2392 3f6a47a8 Michael Hanselmann

2393 3f6a47a8 Michael Hanselmann
  """
2394 3f6a47a8 Michael Hanselmann
  size = 0
2395 3f6a47a8 Michael Hanselmann
2396 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2397 2a887df9 Michael Hanselmann
    for filename in files:
2398 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2399 3f6a47a8 Michael Hanselmann
      size += st.st_size
2400 3f6a47a8 Michael Hanselmann
2401 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2402 3f6a47a8 Michael Hanselmann
2403 3f6a47a8 Michael Hanselmann
2404 620a85fd Iustin Pop
def GetFilesystemStats(path):
2405 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2406 3f6a47a8 Michael Hanselmann

2407 3f6a47a8 Michael Hanselmann
  @type path: string
2408 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2409 3f6a47a8 Michael Hanselmann
  @rtype: int
2410 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2411 3f6a47a8 Michael Hanselmann

2412 3f6a47a8 Michael Hanselmann
  """
2413 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2414 3f6a47a8 Michael Hanselmann
2415 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2416 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2417 620a85fd Iustin Pop
  return (tsize, fsize)
2418 3f6a47a8 Michael Hanselmann
2419 3f6a47a8 Michael Hanselmann
2420 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2421 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2422 eb58f7bd Michael Hanselmann

2423 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2424 eb58f7bd Michael Hanselmann

2425 eb58f7bd Michael Hanselmann
  @type fn: callable
2426 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2427 bdefe5dd Michael Hanselmann
  @rtype: bool
2428 bdefe5dd Michael Hanselmann
  @return: Function's result
2429 eb58f7bd Michael Hanselmann

2430 eb58f7bd Michael Hanselmann
  """
2431 eb58f7bd Michael Hanselmann
  pid = os.fork()
2432 eb58f7bd Michael Hanselmann
  if pid == 0:
2433 eb58f7bd Michael Hanselmann
    # Child process
2434 eb58f7bd Michael Hanselmann
    try:
2435 82869978 Michael Hanselmann
      # In case the function uses temporary files
2436 82869978 Michael Hanselmann
      ResetTempfileModule()
2437 82869978 Michael Hanselmann
2438 eb58f7bd Michael Hanselmann
      # Call function
2439 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2440 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2441 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2442 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2443 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2444 eb58f7bd Michael Hanselmann
      result = 33
2445 eb58f7bd Michael Hanselmann
2446 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2447 eb58f7bd Michael Hanselmann
2448 eb58f7bd Michael Hanselmann
  # Parent process
2449 eb58f7bd Michael Hanselmann
2450 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2451 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2452 eb58f7bd Michael Hanselmann
2453 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2454 eb58f7bd Michael Hanselmann
    exitcode = None
2455 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2456 eb58f7bd Michael Hanselmann
  else:
2457 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2458 eb58f7bd Michael Hanselmann
    signum = None
2459 eb58f7bd Michael Hanselmann
2460 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2461 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2462 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2463 eb58f7bd Michael Hanselmann
2464 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2465 eb58f7bd Michael Hanselmann
2466 eb58f7bd Michael Hanselmann
2467 7996a135 Iustin Pop
def LockedMethod(fn):
2468 7996a135 Iustin Pop
  """Synchronized object access decorator.
2469 7996a135 Iustin Pop

2470 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2471 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2472 7996a135 Iustin Pop

2473 7996a135 Iustin Pop
  """
2474 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2475 e67bd559 Michael Hanselmann
    if debug_locks:
2476 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2477 e67bd559 Michael Hanselmann
2478 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2479 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2480 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2481 7996a135 Iustin Pop
    lock = self._lock
2482 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2483 7996a135 Iustin Pop
    lock.acquire()
2484 7996a135 Iustin Pop
    try:
2485 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2486 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2487 7996a135 Iustin Pop
    finally:
2488 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2489 7996a135 Iustin Pop
      lock.release()
2490 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2491 7996a135 Iustin Pop
    return result
2492 7996a135 Iustin Pop
  return wrapper
2493 eb0f0ce0 Michael Hanselmann
2494 eb0f0ce0 Michael Hanselmann
2495 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2496 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2497 eb0f0ce0 Michael Hanselmann

2498 58885d79 Iustin Pop
  @type fd: int
2499 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2500 58885d79 Iustin Pop

2501 eb0f0ce0 Michael Hanselmann
  """
2502 eb0f0ce0 Michael Hanselmann
  try:
2503 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2504 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2505 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2506 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2507 eb0f0ce0 Michael Hanselmann
    raise
2508 de499029 Michael Hanselmann
2509 de499029 Michael Hanselmann
2510 3b813dd2 Iustin Pop
def FormatTime(val):
2511 3b813dd2 Iustin Pop
  """Formats a time value.
2512 3b813dd2 Iustin Pop

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

2517 3b813dd2 Iustin Pop
  """
2518 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2519 3b813dd2 Iustin Pop
    return "N/A"
2520 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2521 3b813dd2 Iustin Pop
  # platforms
2522 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2523 3b813dd2 Iustin Pop
2524 3b813dd2 Iustin Pop
2525 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2526 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2527 05e50653 Michael Hanselmann

2528 5cbe43a5 Michael Hanselmann
  @type filename: string
2529 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2530 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2531 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2532 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2533 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2534 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2535 5cbe43a5 Michael Hanselmann

2536 05e50653 Michael Hanselmann
  """
2537 05e50653 Michael Hanselmann
  if now is None:
2538 05e50653 Michael Hanselmann
    now = time.time()
2539 05e50653 Michael Hanselmann
2540 05e50653 Michael Hanselmann
  try:
2541 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2542 05e50653 Michael Hanselmann
  except IOError, err:
2543 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2544 05e50653 Michael Hanselmann
      raise
2545 05e50653 Michael Hanselmann
    value = None
2546 05e50653 Michael Hanselmann
2547 05e50653 Michael Hanselmann
  if value is not None:
2548 05e50653 Michael Hanselmann
    try:
2549 05e50653 Michael Hanselmann
      value = int(value)
2550 05e50653 Michael Hanselmann
    except ValueError:
2551 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2552 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2553 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2554 05e50653 Michael Hanselmann
      value = None
2555 05e50653 Michael Hanselmann
2556 05e50653 Michael Hanselmann
    if value is not None:
2557 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2558 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2559 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2560 5cbe43a5 Michael Hanselmann
        value = None
2561 5cbe43a5 Michael Hanselmann
2562 5cbe43a5 Michael Hanselmann
      elif now > value:
2563 05e50653 Michael Hanselmann
        value = None
2564 05e50653 Michael Hanselmann
2565 05e50653 Michael Hanselmann
  return value
2566 05e50653 Michael Hanselmann
2567 05e50653 Michael Hanselmann
2568 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2569 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2570 de0ea66b Michael Hanselmann

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

2575 de0ea66b Michael Hanselmann
  """
2576 506be7c5 Guido Trotter
  def RaiseInner(self):
2577 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
2578 506be7c5 Guido Trotter
      raise self.args[0]
2579 506be7c5 Guido Trotter
    else:
2580 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
2581 de0ea66b Michael Hanselmann
2582 de0ea66b Michael Hanselmann
2583 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2584 de0ea66b Michael Hanselmann
  """Retry again.
2585 de0ea66b Michael Hanselmann

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

2590 de0ea66b Michael Hanselmann
  """
2591 de0ea66b Michael Hanselmann
2592 de0ea66b Michael Hanselmann
2593 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2594 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2595 de0ea66b Michael Hanselmann

2596 de0ea66b Michael Hanselmann
  """
2597 de0ea66b Michael Hanselmann
  __slots__ = [
2598 de0ea66b Michael Hanselmann
    "_factor",
2599 de0ea66b Michael Hanselmann
    "_limit",
2600 de0ea66b Michael Hanselmann
    "_next",
2601 de0ea66b Michael Hanselmann
    "_start",
2602 de0ea66b Michael Hanselmann
    ]
2603 de0ea66b Michael Hanselmann
2604 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2605 de0ea66b Michael Hanselmann
    """Initializes this class.
2606 de0ea66b Michael Hanselmann

2607 de0ea66b Michael Hanselmann
    @type start: float
2608 de0ea66b Michael Hanselmann
    @param start: Initial delay
2609 de0ea66b Michael Hanselmann
    @type factor: float
2610 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2611 de0ea66b Michael Hanselmann
    @type limit: float or None
2612 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2613 de0ea66b Michael Hanselmann

2614 de0ea66b Michael Hanselmann
    """
2615 de0ea66b Michael Hanselmann
    assert start > 0.0
2616 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2617 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2618 de0ea66b Michael Hanselmann
2619 de0ea66b Michael Hanselmann
    self._start = start
2620 de0ea66b Michael Hanselmann
    self._factor = factor
2621 de0ea66b Michael Hanselmann
    self._limit = limit
2622 de0ea66b Michael Hanselmann
2623 de0ea66b Michael Hanselmann
    self._next = start
2624 de0ea66b Michael Hanselmann
2625 de0ea66b Michael Hanselmann
  def __call__(self):
2626 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2627 de0ea66b Michael Hanselmann

2628 de0ea66b Michael Hanselmann
    """
2629 de0ea66b Michael Hanselmann
    current = self._next
2630 de0ea66b Michael Hanselmann
2631 de0ea66b Michael Hanselmann
    # Update for next run
2632 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2633 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2634 de0ea66b Michael Hanselmann
2635 de0ea66b Michael Hanselmann
    return current
2636 de0ea66b Michael Hanselmann
2637 de0ea66b Michael Hanselmann
2638 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2639 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2640 de0ea66b Michael Hanselmann
2641 de0ea66b Michael Hanselmann
2642 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2643 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2644 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2645 de0ea66b Michael Hanselmann

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

2650 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2651 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2652 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2653 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2654 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2655 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2656 de0ea66b Michael Hanselmann

2657 de0ea66b Michael Hanselmann
  @type fn: callable
2658 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2659 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2660 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2661 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2662 de0ea66b Michael Hanselmann
  @type timeout: float
2663 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2664 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2665 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2666 de0ea66b Michael Hanselmann
  @return: Return value of function
2667 de0ea66b Michael Hanselmann

2668 de0ea66b Michael Hanselmann
  """
2669 de0ea66b Michael Hanselmann
  assert callable(fn)
2670 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2671 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2672 de0ea66b Michael Hanselmann
2673 de0ea66b Michael Hanselmann
  if args is None:
2674 de0ea66b Michael Hanselmann
    args = []
2675 de0ea66b Michael Hanselmann
2676 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2677 de0ea66b Michael Hanselmann
2678 de0ea66b Michael Hanselmann
  if callable(delay):
2679 de0ea66b Michael Hanselmann
    # External function to calculate delay
2680 de0ea66b Michael Hanselmann
    calc_delay = delay
2681 de0ea66b Michael Hanselmann
2682 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2683 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2684 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2685 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2686 de0ea66b Michael Hanselmann
2687 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2688 de0ea66b Michael Hanselmann
    # Always use the remaining time
2689 de0ea66b Michael Hanselmann
    calc_delay = None
2690 de0ea66b Michael Hanselmann
2691 de0ea66b Michael Hanselmann
  else:
2692 de0ea66b Michael Hanselmann
    # Static delay
2693 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2694 de0ea66b Michael Hanselmann
2695 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2696 de0ea66b Michael Hanselmann
2697 de0ea66b Michael Hanselmann
  while True:
2698 506be7c5 Guido Trotter
    retry_args = []
2699 de0ea66b Michael Hanselmann
    try:
2700 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2701 de0ea66b Michael Hanselmann
      return fn(*args)
2702 506be7c5 Guido Trotter
    except RetryAgain, err:
2703 506be7c5 Guido Trotter
      retry_args = err.args
2704 1b429e2a Iustin Pop
    except RetryTimeout:
2705 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
2706 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
2707 de0ea66b Michael Hanselmann
2708 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2709 de0ea66b Michael Hanselmann
2710 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2711 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
2712 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
2713 de0ea66b Michael Hanselmann
2714 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2715 de0ea66b Michael Hanselmann
2716 de0ea66b Michael Hanselmann
    if calc_delay is None:
2717 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2718 de0ea66b Michael Hanselmann
    else:
2719 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2720 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2721 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2722 de0ea66b Michael Hanselmann
2723 de0ea66b Michael Hanselmann
2724 a87b4824 Michael Hanselmann
class FileLock(object):
2725 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2726 a87b4824 Michael Hanselmann

2727 a87b4824 Michael Hanselmann
  """
2728 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2729 58885d79 Iustin Pop
    """Constructor for FileLock.
2730 58885d79 Iustin Pop

2731 b4478d34 Michael Hanselmann
    @type fd: file
2732 b4478d34 Michael Hanselmann
    @param fd: File object
2733 58885d79 Iustin Pop
    @type filename: str
2734 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2735 58885d79 Iustin Pop

2736 58885d79 Iustin Pop
    """
2737 b4478d34 Michael Hanselmann
    self.fd = fd
2738 a87b4824 Michael Hanselmann
    self.filename = filename
2739 b4478d34 Michael Hanselmann
2740 b4478d34 Michael Hanselmann
  @classmethod
2741 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2742 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2743 b4478d34 Michael Hanselmann

2744 b4478d34 Michael Hanselmann
    @type filename: string
2745 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2746 b4478d34 Michael Hanselmann

2747 b4478d34 Michael Hanselmann
    """
2748 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2749 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2750 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2751 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2752 b4478d34 Michael Hanselmann
               filename)
2753 a87b4824 Michael Hanselmann
2754 a87b4824 Michael Hanselmann
  def __del__(self):
2755 a87b4824 Michael Hanselmann
    self.Close()
2756 a87b4824 Michael Hanselmann
2757 a87b4824 Michael Hanselmann
  def Close(self):
2758 58885d79 Iustin Pop
    """Close the file and release the lock.
2759 58885d79 Iustin Pop

2760 58885d79 Iustin Pop
    """
2761 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2762 a87b4824 Michael Hanselmann
      self.fd.close()
2763 a87b4824 Michael Hanselmann
      self.fd = None
2764 a87b4824 Michael Hanselmann
2765 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2766 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2767 aa74b828 Michael Hanselmann

2768 aa74b828 Michael Hanselmann
    @type flag: int
2769 58885d79 Iustin Pop
    @param flag: operation flag
2770 aa74b828 Michael Hanselmann
    @type blocking: bool
2771 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2772 aa74b828 Michael Hanselmann
    @type timeout: None or float
2773 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2774 aa74b828 Michael Hanselmann
                    non-blocking mode).
2775 aa74b828 Michael Hanselmann
    @type errmsg: string
2776 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2777 aa74b828 Michael Hanselmann

2778 aa74b828 Michael Hanselmann
    """
2779 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2780 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2781 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2782 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2783 a87b4824 Michael Hanselmann
2784 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2785 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2786 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2787 a87b4824 Michael Hanselmann
2788 cc4c9b91 Michael Hanselmann
    if timeout is None:
2789 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2790 cc4c9b91 Michael Hanselmann
    else:
2791 cc4c9b91 Michael Hanselmann
      try:
2792 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2793 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2794 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2795 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2796 aa74b828 Michael Hanselmann
2797 cc4c9b91 Michael Hanselmann
  @staticmethod
2798 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2799 cc4c9b91 Michael Hanselmann
    try:
2800 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2801 cc4c9b91 Michael Hanselmann
    except IOError, err:
2802 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2803 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2804 31892b4c Michael Hanselmann
2805 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2806 cc4c9b91 Michael Hanselmann
      raise
2807 aa74b828 Michael Hanselmann
2808 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2809 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2810 a87b4824 Michael Hanselmann

2811 58885d79 Iustin Pop
    @type blocking: boolean
2812 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2813 58885d79 Iustin Pop
        can lock the file or return immediately
2814 58885d79 Iustin Pop
    @type timeout: int or None
2815 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2816 58885d79 Iustin Pop
        (in blocking mode)
2817 58885d79 Iustin Pop

2818 a87b4824 Michael Hanselmann
    """
2819 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2820 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2821 a87b4824 Michael Hanselmann
2822 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2823 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2824 a87b4824 Michael Hanselmann

2825 58885d79 Iustin Pop
    @type blocking: boolean
2826 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2827 58885d79 Iustin Pop
        can lock the file or return immediately
2828 58885d79 Iustin Pop
    @type timeout: int or None
2829 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2830 58885d79 Iustin Pop
        (in blocking mode)
2831 58885d79 Iustin Pop

2832 a87b4824 Michael Hanselmann
    """
2833 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2834 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2835 a87b4824 Michael Hanselmann
2836 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2837 a87b4824 Michael Hanselmann
    """Unlocks the file.
2838 a87b4824 Michael Hanselmann

2839 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2840 58885d79 Iustin Pop
    operation::
2841 58885d79 Iustin Pop

2842 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2843 58885d79 Iustin Pop
      operations.
2844 58885d79 Iustin Pop

2845 58885d79 Iustin Pop
    @type blocking: boolean
2846 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2847 58885d79 Iustin Pop
        can lock the file or return immediately
2848 58885d79 Iustin Pop
    @type timeout: int or None
2849 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2850 58885d79 Iustin Pop
        (in blocking mode)
2851 a87b4824 Michael Hanselmann

2852 a87b4824 Michael Hanselmann
    """
2853 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2854 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2855 a87b4824 Michael Hanselmann
2856 a87b4824 Michael Hanselmann
2857 339be5a8 Michael Hanselmann
class LineSplitter:
2858 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
2859 339be5a8 Michael Hanselmann

2860 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
2861 339be5a8 Michael Hanselmann

2862 339be5a8 Michael Hanselmann
  """
2863 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
2864 339be5a8 Michael Hanselmann
    """Initializes this class.
2865 339be5a8 Michael Hanselmann

2866 339be5a8 Michael Hanselmann
    @type line_fn: callable
2867 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
2868 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
2869 339be5a8 Michael Hanselmann

2870 339be5a8 Michael Hanselmann
    """
2871 339be5a8 Michael Hanselmann
    assert callable(line_fn)
2872 339be5a8 Michael Hanselmann
2873 339be5a8 Michael Hanselmann
    if args:
2874 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
2875 339be5a8 Michael Hanselmann
      self._line_fn = \
2876 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
2877 339be5a8 Michael Hanselmann
    else:
2878 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
2879 339be5a8 Michael Hanselmann
2880 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
2881 339be5a8 Michael Hanselmann
    self._buffer = ""
2882 339be5a8 Michael Hanselmann
2883 339be5a8 Michael Hanselmann
  def write(self, data):
2884 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
2885 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
2886 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
2887 339be5a8 Michael Hanselmann
2888 339be5a8 Michael Hanselmann
  def flush(self):
2889 339be5a8 Michael Hanselmann
    while self._lines:
2890 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
2891 339be5a8 Michael Hanselmann
2892 339be5a8 Michael Hanselmann
  def close(self):
2893 339be5a8 Michael Hanselmann
    self.flush()
2894 339be5a8 Michael Hanselmann
    if self._buffer:
2895 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
2896 339be5a8 Michael Hanselmann
2897 339be5a8 Michael Hanselmann
2898 451575de Guido Trotter
def SignalHandled(signums):
2899 451575de Guido Trotter
  """Signal Handled decoration.
2900 451575de Guido Trotter

2901 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2902 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2903 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2904 451575de Guido Trotter
  objects as values.
2905 451575de Guido Trotter

2906 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2907 451575de Guido Trotter
  with different handlers.
2908 451575de Guido Trotter

2909 451575de Guido Trotter
  @type signums: list
2910 451575de Guido Trotter
  @param signums: signals to intercept
2911 451575de Guido Trotter

2912 451575de Guido Trotter
  """
2913 451575de Guido Trotter
  def wrap(fn):
2914 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2915 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2916 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2917 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2918 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2919 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2920 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2921 451575de Guido Trotter
      else:
2922 451575de Guido Trotter
        signal_handlers = {}
2923 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2924 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2925 451575de Guido Trotter
      try:
2926 451575de Guido Trotter
        for sig in signums:
2927 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2928 451575de Guido Trotter
        return fn(*args, **kwargs)
2929 451575de Guido Trotter
      finally:
2930 451575de Guido Trotter
        sighandler.Reset()
2931 451575de Guido Trotter
    return sig_function
2932 451575de Guido Trotter
  return wrap
2933 451575de Guido Trotter
2934 451575de Guido Trotter
2935 de499029 Michael Hanselmann
class SignalHandler(object):
2936 de499029 Michael Hanselmann
  """Generic signal handler class.
2937 de499029 Michael Hanselmann

2938 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2939 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2940 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2941 58885d79 Iustin Pop
  signal was sent.
2942 58885d79 Iustin Pop

2943 58885d79 Iustin Pop
  @type signum: list
2944 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2945 58885d79 Iustin Pop
  @type called: boolean
2946 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2947 de499029 Michael Hanselmann

2948 de499029 Michael Hanselmann
  """
2949 de499029 Michael Hanselmann
  def __init__(self, signum):
2950 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2951 de499029 Michael Hanselmann

2952 58885d79 Iustin Pop
    @type signum: int or list of ints
2953 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2954 de499029 Michael Hanselmann

2955 de499029 Michael Hanselmann
    """
2956 6c52849e Guido Trotter
    self.signum = set(signum)
2957 de499029 Michael Hanselmann
    self.called = False
2958 de499029 Michael Hanselmann
2959 de499029 Michael Hanselmann
    self._previous = {}
2960 de499029 Michael Hanselmann
    try:
2961 de499029 Michael Hanselmann
      for signum in self.signum:
2962 de499029 Michael Hanselmann
        # Setup handler
2963 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2964 de499029 Michael Hanselmann
        try:
2965 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2966 de499029 Michael Hanselmann
        except:
2967 de499029 Michael Hanselmann
          # Restore previous handler
2968 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2969 de499029 Michael Hanselmann
          raise
2970 de499029 Michael Hanselmann
    except:
2971 de499029 Michael Hanselmann
      # Reset all handlers
2972 de499029 Michael Hanselmann
      self.Reset()
2973 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2974 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2975 de499029 Michael Hanselmann
      raise
2976 de499029 Michael Hanselmann
2977 de499029 Michael Hanselmann
  def __del__(self):
2978 de499029 Michael Hanselmann
    self.Reset()
2979 de499029 Michael Hanselmann
2980 de499029 Michael Hanselmann
  def Reset(self):
2981 de499029 Michael Hanselmann
    """Restore previous handler.
2982 de499029 Michael Hanselmann

2983 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2984 58885d79 Iustin Pop

2985 de499029 Michael Hanselmann
    """
2986 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2987 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2988 de499029 Michael Hanselmann
      # If successful, remove from dict
2989 de499029 Michael Hanselmann
      del self._previous[signum]
2990 de499029 Michael Hanselmann
2991 de499029 Michael Hanselmann
  def Clear(self):
2992 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2993 de499029 Michael Hanselmann

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

2996 de499029 Michael Hanselmann
    """
2997 de499029 Michael Hanselmann
    self.called = False
2998 de499029 Michael Hanselmann
2999 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
3000 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
3001 de499029 Michael Hanselmann
    """Actual signal handling function.
3002 de499029 Michael Hanselmann

3003 de499029 Michael Hanselmann
    """
3004 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3005 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3006 de499029 Michael Hanselmann
    self.called = True
3007 a2d2e1a7 Iustin Pop
3008 a2d2e1a7 Iustin Pop
3009 a2d2e1a7 Iustin Pop
class FieldSet(object):
3010 a2d2e1a7 Iustin Pop
  """A simple field set.
3011 a2d2e1a7 Iustin Pop

3012 a2d2e1a7 Iustin Pop
  Among the features are:
3013 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3014 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3015 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3016 a2d2e1a7 Iustin Pop

3017 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3018 a2d2e1a7 Iustin Pop

3019 a2d2e1a7 Iustin Pop
  """
3020 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3021 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3022 a2d2e1a7 Iustin Pop
3023 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3024 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3025 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3026 a2d2e1a7 Iustin Pop
3027 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3028 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3029 a2d2e1a7 Iustin Pop

3030 a2d2e1a7 Iustin Pop
    @type field: str
3031 a2d2e1a7 Iustin Pop
    @param field: the string to match
3032 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3033 a2d2e1a7 Iustin Pop

3034 a2d2e1a7 Iustin Pop
    """
3035 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3036 a2d2e1a7 Iustin Pop
      return m
3037 6c881c52 Iustin Pop
    return None
3038 a2d2e1a7 Iustin Pop
3039 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3040 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3041 a2d2e1a7 Iustin Pop

3042 a2d2e1a7 Iustin Pop
    @type items: list
3043 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3044 a2d2e1a7 Iustin Pop
    @rtype: list
3045 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3046 a2d2e1a7 Iustin Pop

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