Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ b774bb10

History | View | Annotate | Download (80.8 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 a8083063 Iustin Pop
from ganeti import errors
61 3aecd2c7 Iustin Pop
from ganeti import constants
62 a8083063 Iustin Pop
63 16abfbc2 Alexander Schreiber
64 a8083063 Iustin Pop
_locksheld = []
65 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
66 a8083063 Iustin Pop
67 e67bd559 Michael Hanselmann
debug_locks = False
68 58885d79 Iustin Pop
69 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
70 b74159ee Iustin Pop
no_fork = False
71 f362096f Iustin Pop
72 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
73 13998ef2 Michael Hanselmann
74 f93f2016 Michael Hanselmann
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
75 f93f2016 Michael Hanselmann
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
76 f93f2016 Michael Hanselmann
#
77 f93f2016 Michael Hanselmann
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
78 f93f2016 Michael Hanselmann
# pid_t to "int".
79 f93f2016 Michael Hanselmann
#
80 f93f2016 Michael Hanselmann
# IEEE Std 1003.1-2008:
81 f93f2016 Michael Hanselmann
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
82 f93f2016 Michael Hanselmann
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
83 f93f2016 Michael Hanselmann
_STRUCT_UCRED = "iII"
84 f93f2016 Michael Hanselmann
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
85 f93f2016 Michael Hanselmann
86 7c0d6283 Michael Hanselmann
87 a8083063 Iustin Pop
class RunResult(object):
88 58885d79 Iustin Pop
  """Holds the result of running external programs.
89 58885d79 Iustin Pop

90 58885d79 Iustin Pop
  @type exit_code: int
91 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
92 58885d79 Iustin Pop
      didn't exit())
93 58885d79 Iustin Pop
  @type signal: int or None
94 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
95 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
96 58885d79 Iustin Pop
  @type stdout: str
97 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
98 58885d79 Iustin Pop
  @type stderr: str
99 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
100 58885d79 Iustin Pop
  @type failed: boolean
101 58885d79 Iustin Pop
  @ivar failed: True in case the program was
102 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
103 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
104 a8083063 Iustin Pop

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

132 a8083063 Iustin Pop
    """
133 a8083063 Iustin Pop
    return self.stdout + self.stderr
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
136 a8083063 Iustin Pop
137 a8083063 Iustin Pop
138 bf4daac9 Guido Trotter
def RunCmd(cmd, env=None, output=None, cwd='/', reset_env=False):
139 a8083063 Iustin Pop
  """Execute a (shell) command.
140 a8083063 Iustin Pop

141 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
142 a8083063 Iustin Pop
  closed.
143 a8083063 Iustin Pop

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

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

209 36117c2b Iustin Pop
  @type  cmd: string or list
210 36117c2b Iustin Pop
  @param cmd: Command to run
211 36117c2b Iustin Pop
  @type env: dict
212 36117c2b Iustin Pop
  @param env: The environment to use
213 36117c2b Iustin Pop
  @type via_shell: bool
214 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
215 8797df43 Iustin Pop
  @type cwd: string
216 8797df43 Iustin Pop
  @param cwd: the working directory for the program
217 36117c2b Iustin Pop
  @rtype: tuple
218 36117c2b Iustin Pop
  @return: (out, err, status)
219 36117c2b Iustin Pop

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

278 36117c2b Iustin Pop
  @type  cmd: string or list
279 36117c2b Iustin Pop
  @param cmd: Command to run
280 36117c2b Iustin Pop
  @type env: dict
281 36117c2b Iustin Pop
  @param env: The environment to use
282 36117c2b Iustin Pop
  @type via_shell: bool
283 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
284 36117c2b Iustin Pop
  @type output: str
285 36117c2b Iustin Pop
  @param output: the filename in which to save the output
286 8797df43 Iustin Pop
  @type cwd: string
287 8797df43 Iustin Pop
  @param cwd: the working directory for the program
288 36117c2b Iustin Pop
  @rtype: int
289 36117c2b Iustin Pop
  @return: the exit status
290 36117c2b Iustin Pop

291 36117c2b Iustin Pop
  """
292 36117c2b Iustin Pop
  fh = open(output, "a")
293 36117c2b Iustin Pop
  try:
294 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
295 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
296 36117c2b Iustin Pop
                             stdout=fh,
297 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
298 8797df43 Iustin Pop
                             close_fds=True, env=env,
299 8797df43 Iustin Pop
                             cwd=cwd)
300 36117c2b Iustin Pop
301 36117c2b Iustin Pop
    child.stdin.close()
302 36117c2b Iustin Pop
    status = child.wait()
303 36117c2b Iustin Pop
  finally:
304 36117c2b Iustin Pop
    fh.close()
305 36117c2b Iustin Pop
  return status
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
308 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
309 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
310 6bb65e3a Guido Trotter

311 6bb65e3a Guido Trotter
  @type dir_name: string
312 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
313 6bb65e3a Guido Trotter
  @type env: dict
314 6bb65e3a Guido Trotter
  @param env: The environment to use
315 6bb65e3a Guido Trotter
  @type reset_env: boolean
316 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
317 6bb65e3a Guido Trotter
  @rtype: list of tuples
318 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
319 6bb65e3a Guido Trotter

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

348 f93f2016 Michael Hanselmann
  @param sock: Unix socket
349 f93f2016 Michael Hanselmann
  @rtype: tuple; (number, number, number)
350 f93f2016 Michael Hanselmann
  @return: The PID, UID and GID of the connected foreign process.
351 f93f2016 Michael Hanselmann

352 f93f2016 Michael Hanselmann
  """
353 f93f2016 Michael Hanselmann
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
354 f93f2016 Michael Hanselmann
                             _STRUCT_UCRED_SIZE)
355 f93f2016 Michael Hanselmann
  return struct.unpack(_STRUCT_UCRED, peercred)
356 f93f2016 Michael Hanselmann
357 f93f2016 Michael Hanselmann
358 a8083063 Iustin Pop
def RemoveFile(filename):
359 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
360 a8083063 Iustin Pop

361 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
362 a8083063 Iustin Pop
  errors are passed.
363 a8083063 Iustin Pop

364 58885d79 Iustin Pop
  @type filename: str
365 58885d79 Iustin Pop
  @param filename: the file to be removed
366 58885d79 Iustin Pop

367 a8083063 Iustin Pop
  """
368 a8083063 Iustin Pop
  try:
369 a8083063 Iustin Pop
    os.unlink(filename)
370 a8083063 Iustin Pop
  except OSError, err:
371 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
372 a8083063 Iustin Pop
      raise
373 a8083063 Iustin Pop
374 a8083063 Iustin Pop
375 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
376 6e797216 Michael Hanselmann
  """Renames a file.
377 6e797216 Michael Hanselmann

378 6e797216 Michael Hanselmann
  @type old: string
379 6e797216 Michael Hanselmann
  @param old: Original path
380 6e797216 Michael Hanselmann
  @type new: string
381 6e797216 Michael Hanselmann
  @param new: New path
382 6e797216 Michael Hanselmann
  @type mkdir: bool
383 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
384 6e797216 Michael Hanselmann
  @type mkdir_mode: int
385 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
386 6e797216 Michael Hanselmann

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

406 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
407 76e5f8b5 Michael Hanselmann
  before Python 2.5.
408 76e5f8b5 Michael Hanselmann

409 76e5f8b5 Michael Hanselmann
  """
410 76e5f8b5 Michael Hanselmann
  try:
411 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
412 76e5f8b5 Michael Hanselmann
  except OSError, err:
413 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
414 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
415 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
416 76e5f8b5 Michael Hanselmann
      raise
417 76e5f8b5 Michael Hanselmann
418 76e5f8b5 Michael Hanselmann
419 055f822b Michael Hanselmann
def ResetTempfileModule():
420 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
421 055f822b Michael Hanselmann

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

428 055f822b Michael Hanselmann
  """
429 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
430 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
431 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
432 055f822b Michael Hanselmann
    try:
433 055f822b Michael Hanselmann
      # Reset random name generator
434 055f822b Michael Hanselmann
      tempfile._name_sequence = None
435 055f822b Michael Hanselmann
    finally:
436 055f822b Michael Hanselmann
      tempfile._once_lock.release()
437 055f822b Michael Hanselmann
  else:
438 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
439 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
440 055f822b Michael Hanselmann
441 055f822b Michael Hanselmann
442 a8083063 Iustin Pop
def _FingerprintFile(filename):
443 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
444 a8083063 Iustin Pop

445 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
446 a8083063 Iustin Pop
  instead.
447 a8083063 Iustin Pop

448 58885d79 Iustin Pop
  @type filename: str
449 58885d79 Iustin Pop
  @param filename: the filename to checksum
450 58885d79 Iustin Pop
  @rtype: str
451 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
452 58885d79 Iustin Pop
      of the file
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
  """
455 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
456 a8083063 Iustin Pop
    return None
457 a8083063 Iustin Pop
458 a8083063 Iustin Pop
  f = open(filename)
459 a8083063 Iustin Pop
460 7ffe8fba Carlos Valiente
  fp = sha1()
461 a8083063 Iustin Pop
  while True:
462 a8083063 Iustin Pop
    data = f.read(4096)
463 a8083063 Iustin Pop
    if not data:
464 a8083063 Iustin Pop
      break
465 a8083063 Iustin Pop
466 a8083063 Iustin Pop
    fp.update(data)
467 a8083063 Iustin Pop
468 a8083063 Iustin Pop
  return fp.hexdigest()
469 a8083063 Iustin Pop
470 a8083063 Iustin Pop
471 a8083063 Iustin Pop
def FingerprintFiles(files):
472 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
473 a8083063 Iustin Pop

474 58885d79 Iustin Pop
  @type files: list
475 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
476 58885d79 Iustin Pop
  @rtype: dict
477 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
478 58885d79 Iustin Pop
      existing files
479 a8083063 Iustin Pop

480 a8083063 Iustin Pop
  """
481 a8083063 Iustin Pop
  ret = {}
482 a8083063 Iustin Pop
483 a8083063 Iustin Pop
  for filename in files:
484 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
485 a8083063 Iustin Pop
    if cksum:
486 a8083063 Iustin Pop
      ret[filename] = cksum
487 a8083063 Iustin Pop
488 a8083063 Iustin Pop
  return ret
489 a8083063 Iustin Pop
490 a8083063 Iustin Pop
491 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
492 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
493 a5728081 Guido Trotter

494 a5728081 Guido Trotter
  @type target: dict
495 a5728081 Guido Trotter
  @param target: the dict to update
496 a5728081 Guido Trotter
  @type key_types: dict
497 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
498 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
499 a5728081 Guido Trotter
  @type allowed_values: list
500 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
501 a5728081 Guido Trotter

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

561 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
562 44bf25ff Iustin Pop
      will be returned as alive
563 58885d79 Iustin Pop
  @type pid: int
564 58885d79 Iustin Pop
  @param pid: the process ID to check
565 58885d79 Iustin Pop
  @rtype: boolean
566 58885d79 Iustin Pop
  @return: True if the process exists
567 a8083063 Iustin Pop

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

596 58885d79 Iustin Pop
  @type  pidfile: string
597 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
598 58885d79 Iustin Pop
  @rtype: int
599 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
600 d9f311d7 Iustin Pop
           otherwise 0
601 fee80e90 Guido Trotter

602 fee80e90 Guido Trotter
  """
603 fee80e90 Guido Trotter
  try:
604 13998ef2 Michael Hanselmann
    raw_data = ReadFile(pidfile)
605 d9f311d7 Iustin Pop
  except EnvironmentError, err:
606 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
607 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
608 d9f311d7 Iustin Pop
    return 0
609 fee80e90 Guido Trotter
610 fee80e90 Guido Trotter
  try:
611 13998ef2 Michael Hanselmann
    pid = int(raw_data)
612 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
613 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
614 d9f311d7 Iustin Pop
    return 0
615 fee80e90 Guido Trotter
616 d9f311d7 Iustin Pop
  return pid
617 fee80e90 Guido Trotter
618 fee80e90 Guido Trotter
619 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
620 a8083063 Iustin Pop
  """Try to match a name against a list.
621 a8083063 Iustin Pop

622 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
623 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
624 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
625 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
626 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
627 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
628 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
629 a8083063 Iustin Pop

630 58885d79 Iustin Pop
  @type key: str
631 58885d79 Iustin Pop
  @param key: the name to be searched
632 58885d79 Iustin Pop
  @type name_list: list
633 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
634 256eb94b Guido Trotter
  @type case_sensitive: boolean
635 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
636 a8083063 Iustin Pop

637 58885d79 Iustin Pop
  @rtype: None or str
638 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
639 58885d79 Iustin Pop
      otherwise the element from the list which matches
640 a8083063 Iustin Pop

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

668 bcf043c9 Iustin Pop
  """
669 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
670 26288e68 Iustin Pop
671 89e1fc26 Iustin Pop
  def __init__(self, name=None):
672 bcf043c9 Iustin Pop
    """Initialize the host name object.
673 bcf043c9 Iustin Pop

674 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
675 89e1fc26 Iustin Pop
    name.
676 bcf043c9 Iustin Pop

677 bcf043c9 Iustin Pop
    """
678 89e1fc26 Iustin Pop
    if name is None:
679 89e1fc26 Iustin Pop
      name = self.SysName()
680 89e1fc26 Iustin Pop
681 89e1fc26 Iustin Pop
    self.query = name
682 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
683 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
684 bcf043c9 Iustin Pop
685 c8a0948f Michael Hanselmann
  def ShortName(self):
686 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
687 c8a0948f Michael Hanselmann

688 c8a0948f Michael Hanselmann
    """
689 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
690 c8a0948f Michael Hanselmann
691 89e1fc26 Iustin Pop
  @staticmethod
692 89e1fc26 Iustin Pop
  def SysName():
693 89e1fc26 Iustin Pop
    """Return the current system's name.
694 bcf043c9 Iustin Pop

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

697 89e1fc26 Iustin Pop
    """
698 89e1fc26 Iustin Pop
    return socket.gethostname()
699 a8083063 Iustin Pop
700 89e1fc26 Iustin Pop
  @staticmethod
701 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
702 89e1fc26 Iustin Pop
    """Look up hostname
703 a8083063 Iustin Pop

704 58885d79 Iustin Pop
    @type hostname: str
705 58885d79 Iustin Pop
    @param hostname: hostname to look up
706 89e1fc26 Iustin Pop

707 58885d79 Iustin Pop
    @rtype: tuple
708 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
709 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
710 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
711 89e1fc26 Iustin Pop

712 89e1fc26 Iustin Pop
    """
713 89e1fc26 Iustin Pop
    try:
714 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
715 89e1fc26 Iustin Pop
    except socket.gaierror, err:
716 89e1fc26 Iustin Pop
      # hostname not found in DNS
717 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
718 a8083063 Iustin Pop
719 89e1fc26 Iustin Pop
    return result
720 a8083063 Iustin Pop
721 26288e68 Iustin Pop
  @classmethod
722 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
723 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
724 26288e68 Iustin Pop

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

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

756 58885d79 Iustin Pop
  @rtype: dict
757 58885d79 Iustin Pop
  @return:
758 58885d79 Iustin Pop
       Dictionary with keys volume name and values
759 58885d79 Iustin Pop
       the size of the volume
760 a8083063 Iustin Pop

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

784 58885d79 Iustin Pop
  @type bridge: str
785 58885d79 Iustin Pop
  @param bridge: the bridge name to check
786 58885d79 Iustin Pop
  @rtype: boolean
787 58885d79 Iustin Pop
  @return: True if it does
788 a8083063 Iustin Pop

789 a8083063 Iustin Pop
  """
790 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
791 a8083063 Iustin Pop
792 a8083063 Iustin Pop
793 a8083063 Iustin Pop
def NiceSort(name_list):
794 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
795 a8083063 Iustin Pop

796 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
797 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
798 58885d79 Iustin Pop
  'a11']}.
799 a8083063 Iustin Pop

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

804 58885d79 Iustin Pop
  @type name_list: list
805 58885d79 Iustin Pop
  @param name_list: the names to be sorted
806 58885d79 Iustin Pop
  @rtype: list
807 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
808 a8083063 Iustin Pop

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

833 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
834 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
835 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
836 58885d79 Iustin Pop
  exceptions are propagated to the caller.
837 58885d79 Iustin Pop

838 58885d79 Iustin Pop
  @type fn: callable
839 58885d79 Iustin Pop
  @param fn: function to apply to the value
840 58885d79 Iustin Pop
  @param val: the value to be converted
841 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
842 58885d79 Iustin Pop
      otherwise the original value.
843 a8083063 Iustin Pop

844 a8083063 Iustin Pop
  """
845 a8083063 Iustin Pop
  try:
846 a8083063 Iustin Pop
    nv = fn(val)
847 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
848 a8083063 Iustin Pop
    nv = val
849 a8083063 Iustin Pop
  return nv
850 a8083063 Iustin Pop
851 a8083063 Iustin Pop
852 a8083063 Iustin Pop
def IsValidIP(ip):
853 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
854 a8083063 Iustin Pop

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

858 58885d79 Iustin Pop
  @type ip: str
859 58885d79 Iustin Pop
  @param ip: the address to be checked
860 58885d79 Iustin Pop
  @rtype: a regular expression match object
861 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
862 58885d79 Iustin Pop
      address is not valid
863 a8083063 Iustin Pop

864 a8083063 Iustin Pop
  """
865 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
866 58885d79 Iustin Pop
  #TODO: convert and return only boolean
867 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
868 a8083063 Iustin Pop
869 a8083063 Iustin Pop
870 a8083063 Iustin Pop
def IsValidShellParam(word):
871 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
874 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
875 a8083063 Iustin Pop
  the actual command.
876 a8083063 Iustin Pop

877 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
878 a8083063 Iustin Pop
  side.
879 a8083063 Iustin Pop

880 58885d79 Iustin Pop
  @type word: str
881 58885d79 Iustin Pop
  @param word: the word to check
882 58885d79 Iustin Pop
  @rtype: boolean
883 58885d79 Iustin Pop
  @return: True if the word is 'safe'
884 58885d79 Iustin Pop

885 a8083063 Iustin Pop
  """
886 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
887 a8083063 Iustin Pop
888 a8083063 Iustin Pop
889 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
890 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
891 a8083063 Iustin Pop

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

897 58885d79 Iustin Pop
  @type template: str
898 58885d79 Iustin Pop
  @param template: the string holding the template for the
899 58885d79 Iustin Pop
      string formatting
900 58885d79 Iustin Pop
  @rtype: str
901 58885d79 Iustin Pop
  @return: the expanded command line
902 58885d79 Iustin Pop

903 a8083063 Iustin Pop
  """
904 a8083063 Iustin Pop
  for word in args:
905 a8083063 Iustin Pop
    if not IsValidShellParam(word):
906 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
907 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
908 a8083063 Iustin Pop
  return template % args
909 a8083063 Iustin Pop
910 a8083063 Iustin Pop
911 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
912 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
913 a8083063 Iustin Pop

914 58885d79 Iustin Pop
  @type value: int
915 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
916 9fbfbb7b Iustin Pop
  @type units: char
917 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
918 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
919 9fbfbb7b Iustin Pop
      - 'm' for MiBs
920 9fbfbb7b Iustin Pop
      - 'g' for GiBs
921 9fbfbb7b Iustin Pop
      - 't' for TiBs
922 58885d79 Iustin Pop
  @rtype: str
923 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
924 a8083063 Iustin Pop

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

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

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

995 58885d79 Iustin Pop
  @type file_name: str
996 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
997 58885d79 Iustin Pop
  @type key: str
998 58885d79 Iustin Pop
  @param key: string containing key
999 58885d79 Iustin Pop

1000 a8083063 Iustin Pop
  """
1001 a8083063 Iustin Pop
  key_fields = key.split()
1002 a8083063 Iustin Pop
1003 a8083063 Iustin Pop
  f = open(file_name, 'a+')
1004 a8083063 Iustin Pop
  try:
1005 a8083063 Iustin Pop
    nl = True
1006 a8083063 Iustin Pop
    for line in f:
1007 a8083063 Iustin Pop
      # Ignore whitespace changes
1008 a8083063 Iustin Pop
      if line.split() == key_fields:
1009 a8083063 Iustin Pop
        break
1010 a8083063 Iustin Pop
      nl = line.endswith('\n')
1011 a8083063 Iustin Pop
    else:
1012 a8083063 Iustin Pop
      if not nl:
1013 a8083063 Iustin Pop
        f.write("\n")
1014 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1015 a8083063 Iustin Pop
      f.write("\n")
1016 a8083063 Iustin Pop
      f.flush()
1017 a8083063 Iustin Pop
  finally:
1018 a8083063 Iustin Pop
    f.close()
1019 a8083063 Iustin Pop
1020 a8083063 Iustin Pop
1021 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1022 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1023 a8083063 Iustin Pop

1024 58885d79 Iustin Pop
  @type file_name: str
1025 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1026 58885d79 Iustin Pop
  @type key: str
1027 58885d79 Iustin Pop
  @param key: string containing key
1028 58885d79 Iustin Pop

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

1057 58885d79 Iustin Pop
  @type file_name: str
1058 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1059 58885d79 Iustin Pop
  @type ip: str
1060 58885d79 Iustin Pop
  @param ip: the IP address
1061 58885d79 Iustin Pop
  @type hostname: str
1062 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1063 58885d79 Iustin Pop
  @type aliases: list
1064 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1065 58885d79 Iustin Pop

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

1104 58885d79 Iustin Pop
  @type hostname: str
1105 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1106 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1107 58885d79 Iustin Pop

1108 d9c02ca6 Michael Hanselmann
  """
1109 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1110 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1111 d9c02ca6 Michael Hanselmann
1112 d9c02ca6 Michael Hanselmann
1113 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1114 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1115 899d2a81 Michael Hanselmann

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

1118 58885d79 Iustin Pop
  @type file_name: str
1119 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1120 58885d79 Iustin Pop
  @type hostname: str
1121 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1122 58885d79 Iustin Pop

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

1160 58885d79 Iustin Pop
  @type hostname: str
1161 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1162 58885d79 Iustin Pop
      full and shot name will be removed from
1163 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1164 58885d79 Iustin Pop

1165 d9c02ca6 Michael Hanselmann
  """
1166 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1167 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1168 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1169 d9c02ca6 Michael Hanselmann
1170 d9c02ca6 Michael Hanselmann
1171 1d466a4f Michael Hanselmann
def TimestampForFilename():
1172 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1173 1d466a4f Michael Hanselmann

1174 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1175 1d466a4f Michael Hanselmann
  separators.
1176 1d466a4f Michael Hanselmann

1177 1d466a4f Michael Hanselmann
  """
1178 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1179 1d466a4f Michael Hanselmann
1180 1d466a4f Michael Hanselmann
1181 a8083063 Iustin Pop
def CreateBackup(file_name):
1182 a8083063 Iustin Pop
  """Creates a backup of a file.
1183 a8083063 Iustin Pop

1184 58885d79 Iustin Pop
  @type file_name: str
1185 58885d79 Iustin Pop
  @param file_name: file to be backed up
1186 58885d79 Iustin Pop
  @rtype: str
1187 58885d79 Iustin Pop
  @return: the path to the newly created backup
1188 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1189 a8083063 Iustin Pop

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

1217 58885d79 Iustin Pop
  @type value: str
1218 58885d79 Iustin Pop
  @param value: the argument to be quoted
1219 58885d79 Iustin Pop
  @rtype: str
1220 58885d79 Iustin Pop
  @return: the quoted value
1221 58885d79 Iustin Pop

1222 a8083063 Iustin Pop
  """
1223 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1224 a8083063 Iustin Pop
    return value
1225 a8083063 Iustin Pop
  else:
1226 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1227 a8083063 Iustin Pop
1228 a8083063 Iustin Pop
1229 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1230 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1231 58885d79 Iustin Pop

1232 58885d79 Iustin Pop
  @type args: list
1233 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1234 58885d79 Iustin Pop
  @rtype: str
1235 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1236 a8083063 Iustin Pop

1237 a8083063 Iustin Pop
  """
1238 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1239 88d14415 Michael Hanselmann
1240 88d14415 Michael Hanselmann
1241 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1242 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1243 2c30e9d7 Alexander Schreiber

1244 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1245 58885d79 Iustin Pop
  to it.
1246 58885d79 Iustin Pop

1247 58885d79 Iustin Pop
  @type target: str
1248 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1249 58885d79 Iustin Pop
  @type port: int
1250 58885d79 Iustin Pop
  @param port: the port to connect to
1251 58885d79 Iustin Pop
  @type timeout: int
1252 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1253 58885d79 Iustin Pop
  @type live_port_needed: boolean
1254 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1255 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1256 58885d79 Iustin Pop
  @type source: str or None
1257 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1258 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1259 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1260 2c30e9d7 Alexander Schreiber

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

1290 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1291 caad16e2 Iustin Pop
  address.
1292 caad16e2 Iustin Pop

1293 caad16e2 Iustin Pop
  @type address: string
1294 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1295 caad16e2 Iustin Pop
  @rtype: bool
1296 58885d79 Iustin Pop
  @return: True if we own the address
1297 caad16e2 Iustin Pop

1298 caad16e2 Iustin Pop
  """
1299 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1300 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1301 caad16e2 Iustin Pop
1302 caad16e2 Iustin Pop
1303 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1304 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1305 58885d79 Iustin Pop

1306 58885d79 Iustin Pop
  @type path: str
1307 58885d79 Iustin Pop
  @param path: the directory to enumerate
1308 58885d79 Iustin Pop
  @rtype: list
1309 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1310 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1311 eedbda4b Michael Hanselmann

1312 eedbda4b Michael Hanselmann
  """
1313 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1314 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1315 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1316 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1317 f3299a07 Michael Hanselmann
  files.sort()
1318 f3299a07 Michael Hanselmann
  return files
1319 2f8b60b3 Iustin Pop
1320 2f8b60b3 Iustin Pop
1321 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1322 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1323 257f4c0a Iustin Pop

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

1328 2f8b60b3 Iustin Pop
  """
1329 2f8b60b3 Iustin Pop
  try:
1330 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1331 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1332 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1333 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1334 257f4c0a Iustin Pop
    else:
1335 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1336 257f4c0a Iustin Pop
                                   type(user))
1337 2f8b60b3 Iustin Pop
  except KeyError:
1338 2f8b60b3 Iustin Pop
    return default
1339 2f8b60b3 Iustin Pop
  return result.pw_dir
1340 59072e7e Michael Hanselmann
1341 59072e7e Michael Hanselmann
1342 24818e8f Michael Hanselmann
def NewUUID():
1343 59072e7e Michael Hanselmann
  """Returns a random UUID.
1344 59072e7e Michael Hanselmann

1345 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1346 58885d79 Iustin Pop
      filesystem.
1347 58885d79 Iustin Pop
  @rtype: str
1348 58885d79 Iustin Pop

1349 59072e7e Michael Hanselmann
  """
1350 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1351 087b34fe Iustin Pop
1352 087b34fe Iustin Pop
1353 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1354 33081d90 Iustin Pop
  """Generates a random secret.
1355 33081d90 Iustin Pop

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

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

1364 33081d90 Iustin Pop
  """
1365 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1366 33081d90 Iustin Pop
1367 33081d90 Iustin Pop
1368 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1369 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1370 9dae41ad Guido Trotter

1371 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1372 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1373 9dae41ad Guido Trotter

1374 9dae41ad Guido Trotter
  """
1375 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1376 9dae41ad Guido Trotter
    try:
1377 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1378 9dae41ad Guido Trotter
    except EnvironmentError, err:
1379 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1380 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1381 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1382 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1383 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1384 9dae41ad Guido Trotter
1385 9dae41ad Guido Trotter
1386 b774bb10 Michael Hanselmann
def ReadFile(file_name, size=-1, oneline=False):
1387 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1388 ca0aa6d0 Michael Hanselmann

1389 016308cb Iustin Pop
  @type size: int
1390 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1391 b774bb10 Michael Hanselmann
  @type oneline: bool
1392 b774bb10 Michael Hanselmann
  @param oneline: Whether to read only one line (newline char is not included)
1393 58885d79 Iustin Pop
  @rtype: str
1394 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1395 ca0aa6d0 Michael Hanselmann

1396 ca0aa6d0 Michael Hanselmann
  """
1397 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1398 ca0aa6d0 Michael Hanselmann
  try:
1399 b774bb10 Michael Hanselmann
    if oneline:
1400 b774bb10 Michael Hanselmann
      data = f.readline(size).rstrip("\r\n")
1401 b774bb10 Michael Hanselmann
    else:
1402 b774bb10 Michael Hanselmann
      data = f.read(size)
1403 ca0aa6d0 Michael Hanselmann
  finally:
1404 ca0aa6d0 Michael Hanselmann
    f.close()
1405 ca0aa6d0 Michael Hanselmann
1406 b774bb10 Michael Hanselmann
  return data
1407 b774bb10 Michael Hanselmann
1408 ca0aa6d0 Michael Hanselmann
1409 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1410 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1411 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1412 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1413 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1414 087b34fe Iustin Pop
  """(Over)write a file atomically.
1415 087b34fe Iustin Pop

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

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

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

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

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

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

1511 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1512 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1513 7b4126b7 Iustin Pop
  value, the index will be returned.
1514 7b4126b7 Iustin Pop

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

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

1520 58885d79 Iustin Pop
  @type seq: sequence
1521 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1522 58885d79 Iustin Pop
  @type base: int
1523 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1524 58885d79 Iustin Pop
  @rtype: int
1525 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1526 7b4126b7 Iustin Pop

1527 7b4126b7 Iustin Pop
  """
1528 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1529 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1530 7b4126b7 Iustin Pop
    if elem > idx + base:
1531 7b4126b7 Iustin Pop
      # idx is not used
1532 7b4126b7 Iustin Pop
      return idx + base
1533 7b4126b7 Iustin Pop
  return None
1534 7b4126b7 Iustin Pop
1535 7b4126b7 Iustin Pop
1536 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1537 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1538 dcd511c8 Guido Trotter

1539 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1540 dfdc4060 Guido Trotter

1541 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1542 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1543 dfdc4060 Guido Trotter
  @type event: integer
1544 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1545 dcd511c8 Guido Trotter
  @type timeout: float or None
1546 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1547 dcd511c8 Guido Trotter
  @rtype: int or None
1548 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1549 dcd511c8 Guido Trotter

1550 dcd511c8 Guido Trotter
  """
1551 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1552 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1553 dcd511c8 Guido Trotter
1554 dcd511c8 Guido Trotter
  if timeout is not None:
1555 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1556 dcd511c8 Guido Trotter
    timeout *= 1000
1557 dcd511c8 Guido Trotter
1558 dcd511c8 Guido Trotter
  poller = select.poll()
1559 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1560 dcd511c8 Guido Trotter
  try:
1561 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1562 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1563 dfdc4060 Guido Trotter
    # every so often.
1564 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1565 dfdc4060 Guido Trotter
  except select.error, err:
1566 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1567 dfdc4060 Guido Trotter
      raise
1568 dfdc4060 Guido Trotter
    io_events = []
1569 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1570 dfdc4060 Guido Trotter
    return io_events[0][1]
1571 dfdc4060 Guido Trotter
  else:
1572 dfdc4060 Guido Trotter
    return None
1573 dfdc4060 Guido Trotter
1574 dfdc4060 Guido Trotter
1575 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1576 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1577 dfdc4060 Guido Trotter

1578 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1579 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1580 dfdc4060 Guido Trotter
  expired.
1581 dfdc4060 Guido Trotter

1582 dfdc4060 Guido Trotter
  """
1583 dfdc4060 Guido Trotter
1584 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1585 dfdc4060 Guido Trotter
    self.timeout = timeout
1586 dfdc4060 Guido Trotter
1587 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1588 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1589 dfdc4060 Guido Trotter
    if result is None:
1590 dfdc4060 Guido Trotter
      raise RetryAgain()
1591 dfdc4060 Guido Trotter
    else:
1592 dfdc4060 Guido Trotter
      return result
1593 dfdc4060 Guido Trotter
1594 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1595 dfdc4060 Guido Trotter
    self.timeout = timeout
1596 dfdc4060 Guido Trotter
1597 dfdc4060 Guido Trotter
1598 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1599 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1600 dfdc4060 Guido Trotter

1601 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1602 dfdc4060 Guido Trotter

1603 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1604 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1605 dfdc4060 Guido Trotter
  @type event: integer
1606 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1607 dfdc4060 Guido Trotter
  @type timeout: float or None
1608 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1609 dfdc4060 Guido Trotter
  @rtype: int or None
1610 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1611 dfdc4060 Guido Trotter

1612 dfdc4060 Guido Trotter
  """
1613 dfdc4060 Guido Trotter
  if timeout is not None:
1614 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1615 1b429e2a Iustin Pop
    try:
1616 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1617 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1618 1b429e2a Iustin Pop
    except RetryTimeout:
1619 1b429e2a Iustin Pop
      result = None
1620 dfdc4060 Guido Trotter
  else:
1621 dfdc4060 Guido Trotter
    result = None
1622 dfdc4060 Guido Trotter
    while result is None:
1623 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1624 dfdc4060 Guido Trotter
  return result
1625 dcd511c8 Guido Trotter
1626 dcd511c8 Guido Trotter
1627 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1628 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1629 f7414041 Michael Hanselmann

1630 f7414041 Michael Hanselmann
  Element order is preserved.
1631 58885d79 Iustin Pop

1632 58885d79 Iustin Pop
  @type seq: sequence
1633 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1634 58885d79 Iustin Pop
  @rtype: list
1635 58885d79 Iustin Pop
  @return: list of unique elements from seq
1636 58885d79 Iustin Pop

1637 f7414041 Michael Hanselmann
  """
1638 f7414041 Michael Hanselmann
  seen = set()
1639 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1640 1862d460 Alexander Schreiber
1641 1862d460 Alexander Schreiber
1642 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
1643 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
1644 1862d460 Alexander Schreiber

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

1648 58885d79 Iustin Pop
  @type mac: str
1649 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1650 82187135 René Nussbaumer
  @rtype: str
1651 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
1652 82187135 René Nussbaumer

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

1655 1862d460 Alexander Schreiber
  """
1656 82187135 René Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1657 82187135 René Nussbaumer
  if not mac_check.match(mac):
1658 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1659 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
1660 82187135 René Nussbaumer
1661 82187135 René Nussbaumer
  return mac.lower()
1662 06009e27 Iustin Pop
1663 06009e27 Iustin Pop
1664 06009e27 Iustin Pop
def TestDelay(duration):
1665 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1666 06009e27 Iustin Pop

1667 58885d79 Iustin Pop
  @type duration: float
1668 58885d79 Iustin Pop
  @param duration: the sleep duration
1669 58885d79 Iustin Pop
  @rtype: boolean
1670 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1671 58885d79 Iustin Pop

1672 06009e27 Iustin Pop
  """
1673 06009e27 Iustin Pop
  if duration < 0:
1674 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1675 06009e27 Iustin Pop
  time.sleep(duration)
1676 38ea42a1 Iustin Pop
  return True, None
1677 8f765069 Iustin Pop
1678 8f765069 Iustin Pop
1679 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1680 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1681 8f765069 Iustin Pop

1682 7d88772a Iustin Pop
  @type fd: int
1683 7d88772a Iustin Pop
  @param fd: the file descriptor
1684 7d88772a Iustin Pop
  @type retries: int
1685 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1686 7d88772a Iustin Pop
      other error than EBADF
1687 7d88772a Iustin Pop

1688 7d88772a Iustin Pop
  """
1689 7d88772a Iustin Pop
  try:
1690 7d88772a Iustin Pop
    os.close(fd)
1691 7d88772a Iustin Pop
  except OSError, err:
1692 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1693 7d88772a Iustin Pop
      if retries > 0:
1694 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1695 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1696 7d88772a Iustin Pop
    # ignore this and go on
1697 7d88772a Iustin Pop
1698 7d88772a Iustin Pop
1699 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1700 7d88772a Iustin Pop
  """Close file descriptors.
1701 7d88772a Iustin Pop

1702 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1703 7d88772a Iustin Pop
  stdin/out/err).
1704 8f765069 Iustin Pop

1705 58885d79 Iustin Pop
  @type noclose_fds: list or None
1706 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1707 58885d79 Iustin Pop
      that should not be closed
1708 58885d79 Iustin Pop

1709 8f765069 Iustin Pop
  """
1710 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1711 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1712 8f765069 Iustin Pop
    try:
1713 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1714 8f765069 Iustin Pop
      if MAXFD < 0:
1715 8f765069 Iustin Pop
        MAXFD = 1024
1716 8f765069 Iustin Pop
    except OSError:
1717 8f765069 Iustin Pop
      MAXFD = 1024
1718 8f765069 Iustin Pop
  else:
1719 8f765069 Iustin Pop
    MAXFD = 1024
1720 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1721 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1722 7d88772a Iustin Pop
    maxfd = MAXFD
1723 7d88772a Iustin Pop
1724 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1725 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1726 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1727 7d88772a Iustin Pop
      continue
1728 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1729 7d88772a Iustin Pop
1730 7d88772a Iustin Pop
1731 7d88772a Iustin Pop
def Daemonize(logfile):
1732 7d88772a Iustin Pop
  """Daemonize the current process.
1733 7d88772a Iustin Pop

1734 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1735 7d88772a Iustin Pop
  runs it in the background as a daemon.
1736 7d88772a Iustin Pop

1737 7d88772a Iustin Pop
  @type logfile: str
1738 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1739 7d88772a Iustin Pop
  @rtype: int
1740 5fcc718f Iustin Pop
  @return: the value zero
1741 7d88772a Iustin Pop

1742 7d88772a Iustin Pop
  """
1743 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1744 7260cfbe Iustin Pop
  # yes, we really want os._exit
1745 7d88772a Iustin Pop
  UMASK = 077
1746 7d88772a Iustin Pop
  WORKDIR = "/"
1747 8f765069 Iustin Pop
1748 8f765069 Iustin Pop
  # this might fail
1749 8f765069 Iustin Pop
  pid = os.fork()
1750 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1751 8f765069 Iustin Pop
    os.setsid()
1752 8f765069 Iustin Pop
    # this might fail
1753 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1754 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1755 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1756 8f765069 Iustin Pop
      os.umask(UMASK)
1757 8f765069 Iustin Pop
    else:
1758 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1759 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1760 8f765069 Iustin Pop
  else:
1761 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1762 8f765069 Iustin Pop
1763 7d88772a Iustin Pop
  for fd in range(3):
1764 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1765 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1766 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1767 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1768 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1769 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1770 7d88772a Iustin Pop
  os.dup2(1, 2)
1771 8f765069 Iustin Pop
  return 0
1772 57c177af Iustin Pop
1773 57c177af Iustin Pop
1774 53beffbb Iustin Pop
def DaemonPidFileName(name):
1775 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1776 58885d79 Iustin Pop

1777 58885d79 Iustin Pop
  @type name: str
1778 58885d79 Iustin Pop
  @param name: the daemon name
1779 58885d79 Iustin Pop
  @rtype: str
1780 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1781 58885d79 Iustin Pop
      daemon name
1782 b330ac0b Guido Trotter

1783 b330ac0b Guido Trotter
  """
1784 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1785 b330ac0b Guido Trotter
1786 b330ac0b Guido Trotter
1787 2826b361 Guido Trotter
def EnsureDaemon(name):
1788 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1789 2826b361 Guido Trotter

1790 2826b361 Guido Trotter
  """
1791 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1792 2826b361 Guido Trotter
  if result.failed:
1793 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1794 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1795 2826b361 Guido Trotter
    return False
1796 2826b361 Guido Trotter
1797 2826b361 Guido Trotter
  return True
1798 2826b361 Guido Trotter
1799 2826b361 Guido Trotter
1800 b330ac0b Guido Trotter
def WritePidFile(name):
1801 b330ac0b Guido Trotter
  """Write the current process pidfile.
1802 b330ac0b Guido Trotter

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

1805 58885d79 Iustin Pop
  @type name: str
1806 58885d79 Iustin Pop
  @param name: the daemon name to use
1807 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1808 58885d79 Iustin Pop
      points to a live process
1809 b330ac0b Guido Trotter

1810 b330ac0b Guido Trotter
  """
1811 b330ac0b Guido Trotter
  pid = os.getpid()
1812 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1813 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1814 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1815 b330ac0b Guido Trotter
1816 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1817 b330ac0b Guido Trotter
1818 b330ac0b Guido Trotter
1819 b330ac0b Guido Trotter
def RemovePidFile(name):
1820 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1821 b330ac0b Guido Trotter

1822 b330ac0b Guido Trotter
  Any errors are ignored.
1823 b330ac0b Guido Trotter

1824 58885d79 Iustin Pop
  @type name: str
1825 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1826 58885d79 Iustin Pop

1827 b330ac0b Guido Trotter
  """
1828 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1829 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1830 b330ac0b Guido Trotter
  try:
1831 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1832 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1833 b330ac0b Guido Trotter
    pass
1834 b330ac0b Guido Trotter
1835 b330ac0b Guido Trotter
1836 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1837 ff5251bc Iustin Pop
                waitpid=False):
1838 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1839 b2a1f511 Iustin Pop

1840 b2a1f511 Iustin Pop
  @type pid: int
1841 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1842 38206f3c Iustin Pop
  @type signal_: int
1843 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1844 b2a1f511 Iustin Pop
  @type timeout: int
1845 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1846 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1847 b2a1f511 Iustin Pop
                  will be done
1848 ff5251bc Iustin Pop
  @type waitpid: boolean
1849 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1850 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1851 ff5251bc Iustin Pop
      would remain as zombie
1852 b2a1f511 Iustin Pop

1853 b2a1f511 Iustin Pop
  """
1854 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1855 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1856 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1857 ff5251bc Iustin Pop
    if wait:
1858 ff5251bc Iustin Pop
      try:
1859 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1860 ff5251bc Iustin Pop
      except OSError:
1861 ff5251bc Iustin Pop
        pass
1862 ff5251bc Iustin Pop
1863 b2a1f511 Iustin Pop
  if pid <= 0:
1864 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1865 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1866 b2a1f511 Iustin Pop
1867 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1868 b2a1f511 Iustin Pop
    return
1869 31892b4c Michael Hanselmann
1870 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1871 31892b4c Michael Hanselmann
1872 b2a1f511 Iustin Pop
  if timeout <= 0:
1873 b2a1f511 Iustin Pop
    return
1874 7167159a Michael Hanselmann
1875 31892b4c Michael Hanselmann
  def _CheckProcess():
1876 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1877 31892b4c Michael Hanselmann
      return
1878 31892b4c Michael Hanselmann
1879 7167159a Michael Hanselmann
    try:
1880 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1881 7167159a Michael Hanselmann
    except OSError:
1882 31892b4c Michael Hanselmann
      raise RetryAgain()
1883 31892b4c Michael Hanselmann
1884 31892b4c Michael Hanselmann
    if result_pid > 0:
1885 31892b4c Michael Hanselmann
      return
1886 31892b4c Michael Hanselmann
1887 31892b4c Michael Hanselmann
    raise RetryAgain()
1888 31892b4c Michael Hanselmann
1889 31892b4c Michael Hanselmann
  try:
1890 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1891 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1892 31892b4c Michael Hanselmann
  except RetryTimeout:
1893 31892b4c Michael Hanselmann
    pass
1894 7167159a Michael Hanselmann
1895 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1896 7167159a Michael Hanselmann
    # Kill process if it's still alive
1897 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1898 b2a1f511 Iustin Pop
1899 b2a1f511 Iustin Pop
1900 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1901 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1902 57c177af Iustin Pop

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

1906 58885d79 Iustin Pop
  @type name: str
1907 58885d79 Iustin Pop
  @param name: the name to look for
1908 58885d79 Iustin Pop
  @type search_path: str
1909 58885d79 Iustin Pop
  @param search_path: location to start at
1910 58885d79 Iustin Pop
  @type test: callable
1911 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1912 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1913 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1914 58885d79 Iustin Pop
  @rtype: str or None
1915 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1916 57c177af Iustin Pop

1917 57c177af Iustin Pop
  """
1918 f95c81bf Iustin Pop
  # validate the filename mask
1919 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1920 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1921 f95c81bf Iustin Pop
                     name)
1922 f95c81bf Iustin Pop
    return None
1923 f95c81bf Iustin Pop
1924 57c177af Iustin Pop
  for dir_name in search_path:
1925 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1926 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1927 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1928 f95c81bf Iustin Pop
    # basename
1929 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1930 57c177af Iustin Pop
      return item_name
1931 57c177af Iustin Pop
  return None
1932 8d1a2a64 Michael Hanselmann
1933 8d1a2a64 Michael Hanselmann
1934 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1935 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1936 8d1a2a64 Michael Hanselmann

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

1940 58885d79 Iustin Pop
  @type vglist: dict
1941 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1942 58885d79 Iustin Pop
  @type vgname: str
1943 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1944 58885d79 Iustin Pop
  @type minsize: int
1945 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1946 58885d79 Iustin Pop
  @rtype: None or str
1947 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1948 8d1a2a64 Michael Hanselmann

1949 8d1a2a64 Michael Hanselmann
  """
1950 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1951 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1952 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1953 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1954 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1955 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1956 8d1a2a64 Michael Hanselmann
  return None
1957 7996a135 Iustin Pop
1958 7996a135 Iustin Pop
1959 45bc5e4a Michael Hanselmann
def SplitTime(value):
1960 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1961 739be818 Michael Hanselmann

1962 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1963 45bc5e4a Michael Hanselmann
  @type value: int or float
1964 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1965 739be818 Michael Hanselmann

1966 739be818 Michael Hanselmann
  """
1967 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1968 45bc5e4a Michael Hanselmann
1969 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1970 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1971 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1972 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1973 45bc5e4a Michael Hanselmann
1974 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1975 739be818 Michael Hanselmann
1976 739be818 Michael Hanselmann
1977 739be818 Michael Hanselmann
def MergeTime(timetuple):
1978 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1979 739be818 Michael Hanselmann

1980 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1981 739be818 Michael Hanselmann
  @type timetuple: tuple
1982 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1983 739be818 Michael Hanselmann

1984 739be818 Michael Hanselmann
  """
1985 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1986 739be818 Michael Hanselmann
1987 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1988 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1989 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1990 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1991 739be818 Michael Hanselmann
1992 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1993 739be818 Michael Hanselmann
1994 739be818 Michael Hanselmann
1995 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1996 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1997 4a8b186a Michael Hanselmann

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

2002 cd50653c Guido Trotter
  @type daemon_name: string
2003 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2004 58885d79 Iustin Pop
  @rtype: int
2005 58885d79 Iustin Pop

2006 4a8b186a Michael Hanselmann
  """
2007 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
2008 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
2009 cd50653c Guido Trotter
2010 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
2011 4a8b186a Michael Hanselmann
  try:
2012 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
2013 4a8b186a Michael Hanselmann
  except socket.error:
2014 cd50653c Guido Trotter
    port = default_port
2015 4a8b186a Michael Hanselmann
2016 4a8b186a Michael Hanselmann
  return port
2017 4a8b186a Michael Hanselmann
2018 4a8b186a Michael Hanselmann
2019 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2020 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
2021 82d9caef Iustin Pop
  """Configures the logging module.
2022 82d9caef Iustin Pop

2023 58885d79 Iustin Pop
  @type logfile: str
2024 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2025 ea34193f Iustin Pop
  @type debug: integer
2026 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2027 58885d79 Iustin Pop
      only those at C{INFO} and above level
2028 58885d79 Iustin Pop
  @type stderr_logging: boolean
2029 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2030 58885d79 Iustin Pop
  @type program: str
2031 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2032 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2033 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2034 551b6283 Iustin Pop
  @type syslog: string
2035 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2036 551b6283 Iustin Pop
      - if no, syslog is not used
2037 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2038 551b6283 Iustin Pop
      - if only, only syslog is used
2039 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2040 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2041 58885d79 Iustin Pop

2042 82d9caef Iustin Pop
  """
2043 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2044 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2045 d21d09d6 Iustin Pop
  if multithreaded:
2046 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2047 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2048 82d9caef Iustin Pop
  if debug:
2049 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2050 551b6283 Iustin Pop
    # no debug info for syslog loggers
2051 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2052 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2053 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2054 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2055 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2056 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2057 82d9caef Iustin Pop
2058 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2059 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2060 82d9caef Iustin Pop
2061 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2062 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2063 7d88772a Iustin Pop
    handler.close()
2064 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2065 6346a9e5 Michael Hanselmann
2066 82d9caef Iustin Pop
  if stderr_logging:
2067 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2068 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2069 82d9caef Iustin Pop
    if debug:
2070 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2071 82d9caef Iustin Pop
    else:
2072 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2073 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2074 82d9caef Iustin Pop
2075 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2076 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2077 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2078 551b6283 Iustin Pop
                                                    facility)
2079 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2080 551b6283 Iustin Pop
    # Never enable debug over syslog
2081 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2082 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2083 551b6283 Iustin Pop
2084 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2085 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2086 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2087 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2088 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2089 551b6283 Iustin Pop
    try:
2090 551b6283 Iustin Pop
      logfile_handler = logging.FileHandler(logfile)
2091 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2092 551b6283 Iustin Pop
      if debug:
2093 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2094 551b6283 Iustin Pop
      else:
2095 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2096 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2097 551b6283 Iustin Pop
    except EnvironmentError:
2098 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2099 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2100 551b6283 Iustin Pop
      else:
2101 551b6283 Iustin Pop
        # we need to re-raise the exception
2102 551b6283 Iustin Pop
        raise
2103 82d9caef Iustin Pop
2104 016d04b3 Michael Hanselmann
2105 da961187 Guido Trotter
def IsNormAbsPath(path):
2106 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2107 da961187 Guido Trotter

2108 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2109 da961187 Guido Trotter

2110 da961187 Guido Trotter
  """
2111 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2112 82d9caef Iustin Pop
2113 016d04b3 Michael Hanselmann
2114 4bb678e9 Iustin Pop
def PathJoin(*args):
2115 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2116 4bb678e9 Iustin Pop

2117 4bb678e9 Iustin Pop
  Requirements:
2118 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2119 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2120 4bb678e9 Iustin Pop
        since we check for normalization at the end
2121 4bb678e9 Iustin Pop

2122 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2123 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2124 4bb678e9 Iustin Pop

2125 4bb678e9 Iustin Pop
  """
2126 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2127 4bb678e9 Iustin Pop
  assert args
2128 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2129 4bb678e9 Iustin Pop
  root = args[0]
2130 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2131 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2132 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2133 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2134 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2135 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2136 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2137 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2138 4bb678e9 Iustin Pop
  if prefix != root:
2139 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2140 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2141 4bb678e9 Iustin Pop
  return result
2142 4bb678e9 Iustin Pop
2143 4bb678e9 Iustin Pop
2144 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2145 f65f63ef Iustin Pop
  """Return the last lines from a file.
2146 f65f63ef Iustin Pop

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

2151 f65f63ef Iustin Pop
  @param fname: the file name
2152 f65f63ef Iustin Pop
  @type lines: int
2153 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2154 f65f63ef Iustin Pop

2155 f65f63ef Iustin Pop
  """
2156 f65f63ef Iustin Pop
  fd = open(fname, "r")
2157 f65f63ef Iustin Pop
  try:
2158 f65f63ef Iustin Pop
    fd.seek(0, 2)
2159 f65f63ef Iustin Pop
    pos = fd.tell()
2160 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2161 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2162 f65f63ef Iustin Pop
    raw_data = fd.read()
2163 f65f63ef Iustin Pop
  finally:
2164 f65f63ef Iustin Pop
    fd.close()
2165 f65f63ef Iustin Pop
2166 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2167 f65f63ef Iustin Pop
  return rows[-lines:]
2168 f65f63ef Iustin Pop
2169 f65f63ef Iustin Pop
2170 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2171 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2172 27e46076 Michael Hanselmann

2173 27e46076 Michael Hanselmann
  @type value: string
2174 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2175 27e46076 Michael Hanselmann

2176 27e46076 Michael Hanselmann
  """
2177 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2178 27e46076 Michael Hanselmann
  if m:
2179 27e46076 Michael Hanselmann
    # We have an offset
2180 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2181 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2182 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2183 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2184 27e46076 Michael Hanselmann
  else:
2185 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2186 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2187 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2188 27e46076 Michael Hanselmann
    utcoffset = 0
2189 27e46076 Michael Hanselmann
2190 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2191 27e46076 Michael Hanselmann
2192 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2193 27e46076 Michael Hanselmann
2194 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2195 27e46076 Michael Hanselmann
2196 27e46076 Michael Hanselmann
2197 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2198 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2199 27e46076 Michael Hanselmann

2200 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2201 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2202 27e46076 Michael Hanselmann

2203 27e46076 Michael Hanselmann
  """
2204 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2205 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2206 27e46076 Michael Hanselmann
  try:
2207 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2208 27e46076 Michael Hanselmann
  except AttributeError:
2209 27e46076 Michael Hanselmann
    not_before = None
2210 27e46076 Michael Hanselmann
  else:
2211 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2212 27e46076 Michael Hanselmann
2213 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2214 27e46076 Michael Hanselmann
      not_before = None
2215 27e46076 Michael Hanselmann
    else:
2216 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2217 27e46076 Michael Hanselmann
2218 27e46076 Michael Hanselmann
  try:
2219 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2220 27e46076 Michael Hanselmann
  except AttributeError:
2221 27e46076 Michael Hanselmann
    not_after = None
2222 27e46076 Michael Hanselmann
  else:
2223 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2224 27e46076 Michael Hanselmann
2225 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2226 27e46076 Michael Hanselmann
      not_after = None
2227 27e46076 Michael Hanselmann
    else:
2228 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2229 27e46076 Michael Hanselmann
2230 27e46076 Michael Hanselmann
  return (not_before, not_after)
2231 27e46076 Michael Hanselmann
2232 27e46076 Michael Hanselmann
2233 26f15862 Iustin Pop
def SafeEncode(text):
2234 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2235 26f15862 Iustin Pop

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

2245 26f15862 Iustin Pop
  @type text: str or unicode
2246 26f15862 Iustin Pop
  @param text: input data
2247 26f15862 Iustin Pop
  @rtype: str
2248 26f15862 Iustin Pop
  @return: a safe version of text
2249 26f15862 Iustin Pop

2250 26f15862 Iustin Pop
  """
2251 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2252 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2253 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2254 d392fa34 Iustin Pop
  resu = ""
2255 d392fa34 Iustin Pop
  for char in text:
2256 d392fa34 Iustin Pop
    c = ord(char)
2257 d392fa34 Iustin Pop
    if char  == '\t':
2258 d392fa34 Iustin Pop
      resu += r'\t'
2259 d392fa34 Iustin Pop
    elif char == '\n':
2260 d392fa34 Iustin Pop
      resu += r'\n'
2261 d392fa34 Iustin Pop
    elif char == '\r':
2262 d392fa34 Iustin Pop
      resu += r'\'r'
2263 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2264 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2265 d392fa34 Iustin Pop
    else:
2266 d392fa34 Iustin Pop
      resu += char
2267 d392fa34 Iustin Pop
  return resu
2268 26f15862 Iustin Pop
2269 26f15862 Iustin Pop
2270 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2271 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2272 5b69bc7c Iustin Pop

2273 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2274 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2275 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2276 5b69bc7c Iustin Pop
  separator):
2277 5b69bc7c Iustin Pop
    - a plain , separates the elements
2278 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2279 5b69bc7c Iustin Pop
      backslash plus a separator comma
2280 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2281 5b69bc7c Iustin Pop
      non-separator comma
2282 5b69bc7c Iustin Pop

2283 5b69bc7c Iustin Pop
  @type text: string
2284 5b69bc7c Iustin Pop
  @param text: the string to split
2285 5b69bc7c Iustin Pop
  @type sep: string
2286 5b69bc7c Iustin Pop
  @param text: the separator
2287 5b69bc7c Iustin Pop
  @rtype: string
2288 5b69bc7c Iustin Pop
  @return: a list of strings
2289 5b69bc7c Iustin Pop

2290 5b69bc7c Iustin Pop
  """
2291 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2292 5b69bc7c Iustin Pop
  slist = text.split(sep)
2293 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2294 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2295 5b69bc7c Iustin Pop
  rlist = []
2296 5b69bc7c Iustin Pop
  while slist:
2297 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2298 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2299 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2300 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2301 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2302 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2303 5b69bc7c Iustin Pop
        # the next step
2304 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2305 5b69bc7c Iustin Pop
        continue
2306 5b69bc7c Iustin Pop
    rlist.append(e1)
2307 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2308 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2309 5b69bc7c Iustin Pop
  return rlist
2310 5b69bc7c Iustin Pop
2311 5b69bc7c Iustin Pop
2312 ab3e6da8 Iustin Pop
def CommaJoin(names):
2313 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2314 ab3e6da8 Iustin Pop

2315 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2316 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2317 ab3e6da8 Iustin Pop

2318 ab3e6da8 Iustin Pop
  """
2319 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2320 ab3e6da8 Iustin Pop
2321 ab3e6da8 Iustin Pop
2322 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2323 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2324 3f6a47a8 Michael Hanselmann

2325 3f6a47a8 Michael Hanselmann
  @type value: int
2326 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2327 3f6a47a8 Michael Hanselmann
  @rtype: int
2328 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2329 3f6a47a8 Michael Hanselmann

2330 3f6a47a8 Michael Hanselmann
  """
2331 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2332 3f6a47a8 Michael Hanselmann
2333 3f6a47a8 Michael Hanselmann
2334 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2335 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2336 3f6a47a8 Michael Hanselmann

2337 3f6a47a8 Michael Hanselmann
  @type path: string
2338 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2339 3f6a47a8 Michael Hanselmann
  @rtype: int
2340 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2341 3f6a47a8 Michael Hanselmann

2342 3f6a47a8 Michael Hanselmann
  """
2343 3f6a47a8 Michael Hanselmann
  size = 0
2344 3f6a47a8 Michael Hanselmann
2345 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2346 2a887df9 Michael Hanselmann
    for filename in files:
2347 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2348 3f6a47a8 Michael Hanselmann
      size += st.st_size
2349 3f6a47a8 Michael Hanselmann
2350 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2351 3f6a47a8 Michael Hanselmann
2352 3f6a47a8 Michael Hanselmann
2353 620a85fd Iustin Pop
def GetFilesystemStats(path):
2354 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2355 3f6a47a8 Michael Hanselmann

2356 3f6a47a8 Michael Hanselmann
  @type path: string
2357 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2358 3f6a47a8 Michael Hanselmann
  @rtype: int
2359 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2360 3f6a47a8 Michael Hanselmann

2361 3f6a47a8 Michael Hanselmann
  """
2362 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2363 3f6a47a8 Michael Hanselmann
2364 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2365 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2366 620a85fd Iustin Pop
  return (tsize, fsize)
2367 3f6a47a8 Michael Hanselmann
2368 3f6a47a8 Michael Hanselmann
2369 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2370 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2371 eb58f7bd Michael Hanselmann

2372 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2373 eb58f7bd Michael Hanselmann

2374 eb58f7bd Michael Hanselmann
  @type fn: callable
2375 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2376 bdefe5dd Michael Hanselmann
  @rtype: bool
2377 bdefe5dd Michael Hanselmann
  @return: Function's result
2378 eb58f7bd Michael Hanselmann

2379 eb58f7bd Michael Hanselmann
  """
2380 eb58f7bd Michael Hanselmann
  pid = os.fork()
2381 eb58f7bd Michael Hanselmann
  if pid == 0:
2382 eb58f7bd Michael Hanselmann
    # Child process
2383 eb58f7bd Michael Hanselmann
    try:
2384 82869978 Michael Hanselmann
      # In case the function uses temporary files
2385 82869978 Michael Hanselmann
      ResetTempfileModule()
2386 82869978 Michael Hanselmann
2387 eb58f7bd Michael Hanselmann
      # Call function
2388 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2389 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2390 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2391 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2392 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2393 eb58f7bd Michael Hanselmann
      result = 33
2394 eb58f7bd Michael Hanselmann
2395 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2396 eb58f7bd Michael Hanselmann
2397 eb58f7bd Michael Hanselmann
  # Parent process
2398 eb58f7bd Michael Hanselmann
2399 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2400 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2401 eb58f7bd Michael Hanselmann
2402 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2403 eb58f7bd Michael Hanselmann
    exitcode = None
2404 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2405 eb58f7bd Michael Hanselmann
  else:
2406 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2407 eb58f7bd Michael Hanselmann
    signum = None
2408 eb58f7bd Michael Hanselmann
2409 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2410 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2411 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2412 eb58f7bd Michael Hanselmann
2413 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2414 eb58f7bd Michael Hanselmann
2415 eb58f7bd Michael Hanselmann
2416 7996a135 Iustin Pop
def LockedMethod(fn):
2417 7996a135 Iustin Pop
  """Synchronized object access decorator.
2418 7996a135 Iustin Pop

2419 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2420 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2421 7996a135 Iustin Pop

2422 7996a135 Iustin Pop
  """
2423 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2424 e67bd559 Michael Hanselmann
    if debug_locks:
2425 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2426 e67bd559 Michael Hanselmann
2427 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2428 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2429 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2430 7996a135 Iustin Pop
    lock = self._lock
2431 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2432 7996a135 Iustin Pop
    lock.acquire()
2433 7996a135 Iustin Pop
    try:
2434 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2435 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2436 7996a135 Iustin Pop
    finally:
2437 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2438 7996a135 Iustin Pop
      lock.release()
2439 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2440 7996a135 Iustin Pop
    return result
2441 7996a135 Iustin Pop
  return wrapper
2442 eb0f0ce0 Michael Hanselmann
2443 eb0f0ce0 Michael Hanselmann
2444 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2445 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2446 eb0f0ce0 Michael Hanselmann

2447 58885d79 Iustin Pop
  @type fd: int
2448 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2449 58885d79 Iustin Pop

2450 eb0f0ce0 Michael Hanselmann
  """
2451 eb0f0ce0 Michael Hanselmann
  try:
2452 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2453 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2454 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2455 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2456 eb0f0ce0 Michael Hanselmann
    raise
2457 de499029 Michael Hanselmann
2458 de499029 Michael Hanselmann
2459 3b813dd2 Iustin Pop
def FormatTime(val):
2460 3b813dd2 Iustin Pop
  """Formats a time value.
2461 3b813dd2 Iustin Pop

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

2466 3b813dd2 Iustin Pop
  """
2467 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2468 3b813dd2 Iustin Pop
    return "N/A"
2469 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2470 3b813dd2 Iustin Pop
  # platforms
2471 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2472 3b813dd2 Iustin Pop
2473 3b813dd2 Iustin Pop
2474 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2475 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2476 05e50653 Michael Hanselmann

2477 5cbe43a5 Michael Hanselmann
  @type filename: string
2478 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2479 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2480 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2481 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2482 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2483 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2484 5cbe43a5 Michael Hanselmann

2485 05e50653 Michael Hanselmann
  """
2486 05e50653 Michael Hanselmann
  if now is None:
2487 05e50653 Michael Hanselmann
    now = time.time()
2488 05e50653 Michael Hanselmann
2489 05e50653 Michael Hanselmann
  try:
2490 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2491 05e50653 Michael Hanselmann
  except IOError, err:
2492 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2493 05e50653 Michael Hanselmann
      raise
2494 05e50653 Michael Hanselmann
    value = None
2495 05e50653 Michael Hanselmann
2496 05e50653 Michael Hanselmann
  if value is not None:
2497 05e50653 Michael Hanselmann
    try:
2498 05e50653 Michael Hanselmann
      value = int(value)
2499 05e50653 Michael Hanselmann
    except ValueError:
2500 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2501 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2502 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2503 05e50653 Michael Hanselmann
      value = None
2504 05e50653 Michael Hanselmann
2505 05e50653 Michael Hanselmann
    if value is not None:
2506 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2507 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2508 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2509 5cbe43a5 Michael Hanselmann
        value = None
2510 5cbe43a5 Michael Hanselmann
2511 5cbe43a5 Michael Hanselmann
      elif now > value:
2512 05e50653 Michael Hanselmann
        value = None
2513 05e50653 Michael Hanselmann
2514 05e50653 Michael Hanselmann
  return value
2515 05e50653 Michael Hanselmann
2516 05e50653 Michael Hanselmann
2517 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2518 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2519 de0ea66b Michael Hanselmann

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

2524 de0ea66b Michael Hanselmann
  """
2525 506be7c5 Guido Trotter
  def RaiseInner(self):
2526 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
2527 506be7c5 Guido Trotter
      raise self.args[0]
2528 506be7c5 Guido Trotter
    else:
2529 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
2530 de0ea66b Michael Hanselmann
2531 de0ea66b Michael Hanselmann
2532 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2533 de0ea66b Michael Hanselmann
  """Retry again.
2534 de0ea66b Michael Hanselmann

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

2539 de0ea66b Michael Hanselmann
  """
2540 de0ea66b Michael Hanselmann
2541 de0ea66b Michael Hanselmann
2542 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2543 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2544 de0ea66b Michael Hanselmann

2545 de0ea66b Michael Hanselmann
  """
2546 de0ea66b Michael Hanselmann
  __slots__ = [
2547 de0ea66b Michael Hanselmann
    "_factor",
2548 de0ea66b Michael Hanselmann
    "_limit",
2549 de0ea66b Michael Hanselmann
    "_next",
2550 de0ea66b Michael Hanselmann
    "_start",
2551 de0ea66b Michael Hanselmann
    ]
2552 de0ea66b Michael Hanselmann
2553 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2554 de0ea66b Michael Hanselmann
    """Initializes this class.
2555 de0ea66b Michael Hanselmann

2556 de0ea66b Michael Hanselmann
    @type start: float
2557 de0ea66b Michael Hanselmann
    @param start: Initial delay
2558 de0ea66b Michael Hanselmann
    @type factor: float
2559 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2560 de0ea66b Michael Hanselmann
    @type limit: float or None
2561 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2562 de0ea66b Michael Hanselmann

2563 de0ea66b Michael Hanselmann
    """
2564 de0ea66b Michael Hanselmann
    assert start > 0.0
2565 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2566 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2567 de0ea66b Michael Hanselmann
2568 de0ea66b Michael Hanselmann
    self._start = start
2569 de0ea66b Michael Hanselmann
    self._factor = factor
2570 de0ea66b Michael Hanselmann
    self._limit = limit
2571 de0ea66b Michael Hanselmann
2572 de0ea66b Michael Hanselmann
    self._next = start
2573 de0ea66b Michael Hanselmann
2574 de0ea66b Michael Hanselmann
  def __call__(self):
2575 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2576 de0ea66b Michael Hanselmann

2577 de0ea66b Michael Hanselmann
    """
2578 de0ea66b Michael Hanselmann
    current = self._next
2579 de0ea66b Michael Hanselmann
2580 de0ea66b Michael Hanselmann
    # Update for next run
2581 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2582 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2583 de0ea66b Michael Hanselmann
2584 de0ea66b Michael Hanselmann
    return current
2585 de0ea66b Michael Hanselmann
2586 de0ea66b Michael Hanselmann
2587 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2588 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2589 de0ea66b Michael Hanselmann
2590 de0ea66b Michael Hanselmann
2591 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2592 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2593 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2594 de0ea66b Michael Hanselmann

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

2599 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2600 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2601 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2602 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2603 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2604 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2605 de0ea66b Michael Hanselmann

2606 de0ea66b Michael Hanselmann
  @type fn: callable
2607 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2608 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2609 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2610 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2611 de0ea66b Michael Hanselmann
  @type timeout: float
2612 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2613 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2614 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2615 de0ea66b Michael Hanselmann
  @return: Return value of function
2616 de0ea66b Michael Hanselmann

2617 de0ea66b Michael Hanselmann
  """
2618 de0ea66b Michael Hanselmann
  assert callable(fn)
2619 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2620 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2621 de0ea66b Michael Hanselmann
2622 de0ea66b Michael Hanselmann
  if args is None:
2623 de0ea66b Michael Hanselmann
    args = []
2624 de0ea66b Michael Hanselmann
2625 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2626 de0ea66b Michael Hanselmann
2627 de0ea66b Michael Hanselmann
  if callable(delay):
2628 de0ea66b Michael Hanselmann
    # External function to calculate delay
2629 de0ea66b Michael Hanselmann
    calc_delay = delay
2630 de0ea66b Michael Hanselmann
2631 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2632 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2633 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2634 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2635 de0ea66b Michael Hanselmann
2636 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2637 de0ea66b Michael Hanselmann
    # Always use the remaining time
2638 de0ea66b Michael Hanselmann
    calc_delay = None
2639 de0ea66b Michael Hanselmann
2640 de0ea66b Michael Hanselmann
  else:
2641 de0ea66b Michael Hanselmann
    # Static delay
2642 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2643 de0ea66b Michael Hanselmann
2644 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2645 de0ea66b Michael Hanselmann
2646 de0ea66b Michael Hanselmann
  while True:
2647 506be7c5 Guido Trotter
    retry_args = []
2648 de0ea66b Michael Hanselmann
    try:
2649 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2650 de0ea66b Michael Hanselmann
      return fn(*args)
2651 506be7c5 Guido Trotter
    except RetryAgain, err:
2652 506be7c5 Guido Trotter
      retry_args = err.args
2653 1b429e2a Iustin Pop
    except RetryTimeout:
2654 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
2655 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
2656 de0ea66b Michael Hanselmann
2657 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2658 de0ea66b Michael Hanselmann
2659 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2660 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
2661 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
2662 de0ea66b Michael Hanselmann
2663 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2664 de0ea66b Michael Hanselmann
2665 de0ea66b Michael Hanselmann
    if calc_delay is None:
2666 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2667 de0ea66b Michael Hanselmann
    else:
2668 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2669 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2670 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2671 de0ea66b Michael Hanselmann
2672 de0ea66b Michael Hanselmann
2673 a87b4824 Michael Hanselmann
class FileLock(object):
2674 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2675 a87b4824 Michael Hanselmann

2676 a87b4824 Michael Hanselmann
  """
2677 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2678 58885d79 Iustin Pop
    """Constructor for FileLock.
2679 58885d79 Iustin Pop

2680 b4478d34 Michael Hanselmann
    @type fd: file
2681 b4478d34 Michael Hanselmann
    @param fd: File object
2682 58885d79 Iustin Pop
    @type filename: str
2683 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2684 58885d79 Iustin Pop

2685 58885d79 Iustin Pop
    """
2686 b4478d34 Michael Hanselmann
    self.fd = fd
2687 a87b4824 Michael Hanselmann
    self.filename = filename
2688 b4478d34 Michael Hanselmann
2689 b4478d34 Michael Hanselmann
  @classmethod
2690 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2691 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2692 b4478d34 Michael Hanselmann

2693 b4478d34 Michael Hanselmann
    @type filename: string
2694 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2695 b4478d34 Michael Hanselmann

2696 b4478d34 Michael Hanselmann
    """
2697 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2698 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2699 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2700 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2701 b4478d34 Michael Hanselmann
               filename)
2702 a87b4824 Michael Hanselmann
2703 a87b4824 Michael Hanselmann
  def __del__(self):
2704 a87b4824 Michael Hanselmann
    self.Close()
2705 a87b4824 Michael Hanselmann
2706 a87b4824 Michael Hanselmann
  def Close(self):
2707 58885d79 Iustin Pop
    """Close the file and release the lock.
2708 58885d79 Iustin Pop

2709 58885d79 Iustin Pop
    """
2710 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2711 a87b4824 Michael Hanselmann
      self.fd.close()
2712 a87b4824 Michael Hanselmann
      self.fd = None
2713 a87b4824 Michael Hanselmann
2714 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2715 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2716 aa74b828 Michael Hanselmann

2717 aa74b828 Michael Hanselmann
    @type flag: int
2718 58885d79 Iustin Pop
    @param flag: operation flag
2719 aa74b828 Michael Hanselmann
    @type blocking: bool
2720 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2721 aa74b828 Michael Hanselmann
    @type timeout: None or float
2722 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2723 aa74b828 Michael Hanselmann
                    non-blocking mode).
2724 aa74b828 Michael Hanselmann
    @type errmsg: string
2725 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2726 aa74b828 Michael Hanselmann

2727 aa74b828 Michael Hanselmann
    """
2728 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2729 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2730 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2731 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2732 a87b4824 Michael Hanselmann
2733 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2734 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2735 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2736 a87b4824 Michael Hanselmann
2737 cc4c9b91 Michael Hanselmann
    if timeout is None:
2738 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2739 cc4c9b91 Michael Hanselmann
    else:
2740 cc4c9b91 Michael Hanselmann
      try:
2741 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2742 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2743 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2744 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2745 aa74b828 Michael Hanselmann
2746 cc4c9b91 Michael Hanselmann
  @staticmethod
2747 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2748 cc4c9b91 Michael Hanselmann
    try:
2749 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2750 cc4c9b91 Michael Hanselmann
    except IOError, err:
2751 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2752 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2753 31892b4c Michael Hanselmann
2754 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2755 cc4c9b91 Michael Hanselmann
      raise
2756 aa74b828 Michael Hanselmann
2757 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2758 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2759 a87b4824 Michael Hanselmann

2760 58885d79 Iustin Pop
    @type blocking: boolean
2761 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2762 58885d79 Iustin Pop
        can lock the file or return immediately
2763 58885d79 Iustin Pop
    @type timeout: int or None
2764 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2765 58885d79 Iustin Pop
        (in blocking mode)
2766 58885d79 Iustin Pop

2767 a87b4824 Michael Hanselmann
    """
2768 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2769 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2770 a87b4824 Michael Hanselmann
2771 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2772 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2773 a87b4824 Michael Hanselmann

2774 58885d79 Iustin Pop
    @type blocking: boolean
2775 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2776 58885d79 Iustin Pop
        can lock the file or return immediately
2777 58885d79 Iustin Pop
    @type timeout: int or None
2778 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2779 58885d79 Iustin Pop
        (in blocking mode)
2780 58885d79 Iustin Pop

2781 a87b4824 Michael Hanselmann
    """
2782 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2783 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2784 a87b4824 Michael Hanselmann
2785 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2786 a87b4824 Michael Hanselmann
    """Unlocks the file.
2787 a87b4824 Michael Hanselmann

2788 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2789 58885d79 Iustin Pop
    operation::
2790 58885d79 Iustin Pop

2791 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2792 58885d79 Iustin Pop
      operations.
2793 58885d79 Iustin Pop

2794 58885d79 Iustin Pop
    @type blocking: boolean
2795 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2796 58885d79 Iustin Pop
        can lock the file or return immediately
2797 58885d79 Iustin Pop
    @type timeout: int or None
2798 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2799 58885d79 Iustin Pop
        (in blocking mode)
2800 a87b4824 Michael Hanselmann

2801 a87b4824 Michael Hanselmann
    """
2802 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2803 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2804 a87b4824 Michael Hanselmann
2805 a87b4824 Michael Hanselmann
2806 339be5a8 Michael Hanselmann
class LineSplitter:
2807 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
2808 339be5a8 Michael Hanselmann

2809 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
2810 339be5a8 Michael Hanselmann

2811 339be5a8 Michael Hanselmann
  """
2812 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
2813 339be5a8 Michael Hanselmann
    """Initializes this class.
2814 339be5a8 Michael Hanselmann

2815 339be5a8 Michael Hanselmann
    @type line_fn: callable
2816 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
2817 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
2818 339be5a8 Michael Hanselmann

2819 339be5a8 Michael Hanselmann
    """
2820 339be5a8 Michael Hanselmann
    assert callable(line_fn)
2821 339be5a8 Michael Hanselmann
2822 339be5a8 Michael Hanselmann
    if args:
2823 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
2824 339be5a8 Michael Hanselmann
      self._line_fn = \
2825 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
2826 339be5a8 Michael Hanselmann
    else:
2827 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
2828 339be5a8 Michael Hanselmann
2829 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
2830 339be5a8 Michael Hanselmann
    self._buffer = ""
2831 339be5a8 Michael Hanselmann
2832 339be5a8 Michael Hanselmann
  def write(self, data):
2833 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
2834 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
2835 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
2836 339be5a8 Michael Hanselmann
2837 339be5a8 Michael Hanselmann
  def flush(self):
2838 339be5a8 Michael Hanselmann
    while self._lines:
2839 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
2840 339be5a8 Michael Hanselmann
2841 339be5a8 Michael Hanselmann
  def close(self):
2842 339be5a8 Michael Hanselmann
    self.flush()
2843 339be5a8 Michael Hanselmann
    if self._buffer:
2844 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
2845 339be5a8 Michael Hanselmann
2846 339be5a8 Michael Hanselmann
2847 451575de Guido Trotter
def SignalHandled(signums):
2848 451575de Guido Trotter
  """Signal Handled decoration.
2849 451575de Guido Trotter

2850 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2851 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2852 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2853 451575de Guido Trotter
  objects as values.
2854 451575de Guido Trotter

2855 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2856 451575de Guido Trotter
  with different handlers.
2857 451575de Guido Trotter

2858 451575de Guido Trotter
  @type signums: list
2859 451575de Guido Trotter
  @param signums: signals to intercept
2860 451575de Guido Trotter

2861 451575de Guido Trotter
  """
2862 451575de Guido Trotter
  def wrap(fn):
2863 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2864 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2865 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2866 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2867 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2868 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2869 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2870 451575de Guido Trotter
      else:
2871 451575de Guido Trotter
        signal_handlers = {}
2872 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2873 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2874 451575de Guido Trotter
      try:
2875 451575de Guido Trotter
        for sig in signums:
2876 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2877 451575de Guido Trotter
        return fn(*args, **kwargs)
2878 451575de Guido Trotter
      finally:
2879 451575de Guido Trotter
        sighandler.Reset()
2880 451575de Guido Trotter
    return sig_function
2881 451575de Guido Trotter
  return wrap
2882 451575de Guido Trotter
2883 451575de Guido Trotter
2884 de499029 Michael Hanselmann
class SignalHandler(object):
2885 de499029 Michael Hanselmann
  """Generic signal handler class.
2886 de499029 Michael Hanselmann

2887 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2888 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2889 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2890 58885d79 Iustin Pop
  signal was sent.
2891 58885d79 Iustin Pop

2892 58885d79 Iustin Pop
  @type signum: list
2893 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2894 58885d79 Iustin Pop
  @type called: boolean
2895 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2896 de499029 Michael Hanselmann

2897 de499029 Michael Hanselmann
  """
2898 de499029 Michael Hanselmann
  def __init__(self, signum):
2899 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2900 de499029 Michael Hanselmann

2901 58885d79 Iustin Pop
    @type signum: int or list of ints
2902 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2903 de499029 Michael Hanselmann

2904 de499029 Michael Hanselmann
    """
2905 6c52849e Guido Trotter
    self.signum = set(signum)
2906 de499029 Michael Hanselmann
    self.called = False
2907 de499029 Michael Hanselmann
2908 de499029 Michael Hanselmann
    self._previous = {}
2909 de499029 Michael Hanselmann
    try:
2910 de499029 Michael Hanselmann
      for signum in self.signum:
2911 de499029 Michael Hanselmann
        # Setup handler
2912 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2913 de499029 Michael Hanselmann
        try:
2914 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2915 de499029 Michael Hanselmann
        except:
2916 de499029 Michael Hanselmann
          # Restore previous handler
2917 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2918 de499029 Michael Hanselmann
          raise
2919 de499029 Michael Hanselmann
    except:
2920 de499029 Michael Hanselmann
      # Reset all handlers
2921 de499029 Michael Hanselmann
      self.Reset()
2922 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2923 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2924 de499029 Michael Hanselmann
      raise
2925 de499029 Michael Hanselmann
2926 de499029 Michael Hanselmann
  def __del__(self):
2927 de499029 Michael Hanselmann
    self.Reset()
2928 de499029 Michael Hanselmann
2929 de499029 Michael Hanselmann
  def Reset(self):
2930 de499029 Michael Hanselmann
    """Restore previous handler.
2931 de499029 Michael Hanselmann

2932 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2933 58885d79 Iustin Pop

2934 de499029 Michael Hanselmann
    """
2935 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2936 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2937 de499029 Michael Hanselmann
      # If successful, remove from dict
2938 de499029 Michael Hanselmann
      del self._previous[signum]
2939 de499029 Michael Hanselmann
2940 de499029 Michael Hanselmann
  def Clear(self):
2941 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2942 de499029 Michael Hanselmann

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

2945 de499029 Michael Hanselmann
    """
2946 de499029 Michael Hanselmann
    self.called = False
2947 de499029 Michael Hanselmann
2948 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2949 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2950 de499029 Michael Hanselmann
    """Actual signal handling function.
2951 de499029 Michael Hanselmann

2952 de499029 Michael Hanselmann
    """
2953 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2954 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2955 de499029 Michael Hanselmann
    self.called = True
2956 a2d2e1a7 Iustin Pop
2957 a2d2e1a7 Iustin Pop
2958 a2d2e1a7 Iustin Pop
class FieldSet(object):
2959 a2d2e1a7 Iustin Pop
  """A simple field set.
2960 a2d2e1a7 Iustin Pop

2961 a2d2e1a7 Iustin Pop
  Among the features are:
2962 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2963 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2964 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2965 a2d2e1a7 Iustin Pop

2966 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2967 a2d2e1a7 Iustin Pop

2968 a2d2e1a7 Iustin Pop
  """
2969 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2970 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2971 a2d2e1a7 Iustin Pop
2972 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2973 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2974 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2975 a2d2e1a7 Iustin Pop
2976 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2977 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2978 a2d2e1a7 Iustin Pop

2979 a2d2e1a7 Iustin Pop
    @type field: str
2980 a2d2e1a7 Iustin Pop
    @param field: the string to match
2981 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2982 a2d2e1a7 Iustin Pop

2983 a2d2e1a7 Iustin Pop
    """
2984 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2985 a2d2e1a7 Iustin Pop
      return m
2986 6c881c52 Iustin Pop
    return None
2987 a2d2e1a7 Iustin Pop
2988 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2989 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2990 a2d2e1a7 Iustin Pop

2991 a2d2e1a7 Iustin Pop
    @type items: list
2992 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2993 a2d2e1a7 Iustin Pop
    @rtype: list
2994 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2995 a2d2e1a7 Iustin Pop

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