Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 6bb65e3a

History | View | Annotate | Download (70.6 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 9c233417 Iustin Pop
47 9c233417 Iustin Pop
from cStringIO import StringIO
48 a8083063 Iustin Pop
49 7ffe8fba Carlos Valiente
try:
50 7ffe8fba Carlos Valiente
  from hashlib import sha1
51 7ffe8fba Carlos Valiente
except ImportError:
52 7ffe8fba Carlos Valiente
  import sha
53 7ffe8fba Carlos Valiente
  sha1 = sha.new
54 7ffe8fba Carlos Valiente
55 a8083063 Iustin Pop
from ganeti import errors
56 3aecd2c7 Iustin Pop
from ganeti import constants
57 a8083063 Iustin Pop
58 16abfbc2 Alexander Schreiber
59 a8083063 Iustin Pop
_locksheld = []
60 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
61 a8083063 Iustin Pop
62 e67bd559 Michael Hanselmann
debug_locks = False
63 58885d79 Iustin Pop
64 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
65 b74159ee Iustin Pop
no_fork = False
66 f362096f Iustin Pop
67 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
68 13998ef2 Michael Hanselmann
69 7c0d6283 Michael Hanselmann
70 a8083063 Iustin Pop
class RunResult(object):
71 58885d79 Iustin Pop
  """Holds the result of running external programs.
72 58885d79 Iustin Pop

73 58885d79 Iustin Pop
  @type exit_code: int
74 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
75 58885d79 Iustin Pop
      didn't exit())
76 58885d79 Iustin Pop
  @type signal: int or None
77 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
78 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
79 58885d79 Iustin Pop
  @type stdout: str
80 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
81 58885d79 Iustin Pop
  @type stderr: str
82 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
83 58885d79 Iustin Pop
  @type failed: boolean
84 58885d79 Iustin Pop
  @ivar failed: True in case the program was
85 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
86 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
87 a8083063 Iustin Pop

88 a8083063 Iustin Pop
  """
89 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
90 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
91 a8083063 Iustin Pop
92 a8083063 Iustin Pop
93 38206f3c Iustin Pop
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
94 a8083063 Iustin Pop
    self.cmd = cmd
95 a8083063 Iustin Pop
    self.exit_code = exit_code
96 38206f3c Iustin Pop
    self.signal = signal_
97 a8083063 Iustin Pop
    self.stdout = stdout
98 a8083063 Iustin Pop
    self.stderr = stderr
99 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
100 a8083063 Iustin Pop
101 a8083063 Iustin Pop
    if self.signal is not None:
102 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
103 a8083063 Iustin Pop
    elif self.exit_code is not None:
104 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
105 a8083063 Iustin Pop
    else:
106 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
107 a8083063 Iustin Pop
108 bb698c1f Iustin Pop
    if self.failed:
109 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
110 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
111 f362096f Iustin Pop
112 a8083063 Iustin Pop
  def _GetOutput(self):
113 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
114 a8083063 Iustin Pop

115 a8083063 Iustin Pop
    """
116 a8083063 Iustin Pop
    return self.stdout + self.stderr
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
121 bf4daac9 Guido Trotter
def RunCmd(cmd, env=None, output=None, cwd='/', reset_env=False):
122 a8083063 Iustin Pop
  """Execute a (shell) command.
123 a8083063 Iustin Pop

124 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
125 a8083063 Iustin Pop
  closed.
126 a8083063 Iustin Pop

127 bf4daac9 Guido Trotter
  @type cmd: string or list
128 36117c2b Iustin Pop
  @param cmd: Command to run
129 2557ff82 Guido Trotter
  @type env: dict
130 58885d79 Iustin Pop
  @param env: Additional environment
131 36117c2b Iustin Pop
  @type output: str
132 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
133 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
134 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
135 8797df43 Iustin Pop
  @type cwd: string
136 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
137 8797df43 Iustin Pop
      directory for the command; the default will be /
138 bf4daac9 Guido Trotter
  @type reset_env: boolean
139 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
140 36117c2b Iustin Pop
  @rtype: L{RunResult}
141 58885d79 Iustin Pop
  @return: RunResult instance
142 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
143 a8083063 Iustin Pop

144 a8083063 Iustin Pop
  """
145 b74159ee Iustin Pop
  if no_fork:
146 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
147 b74159ee Iustin Pop
148 a8083063 Iustin Pop
  if isinstance(cmd, list):
149 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
150 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
151 113b55aa Iustin Pop
    shell = False
152 113b55aa Iustin Pop
  else:
153 113b55aa Iustin Pop
    strcmd = cmd
154 113b55aa Iustin Pop
    shell = True
155 bb698c1f Iustin Pop
  logging.debug("RunCmd '%s'", strcmd)
156 2557ff82 Guido Trotter
157 bf4daac9 Guido Trotter
  if not reset_env:
158 bf4daac9 Guido Trotter
    cmd_env = os.environ.copy()
159 bf4daac9 Guido Trotter
    cmd_env["LC_ALL"] = "C"
160 bf4daac9 Guido Trotter
  else:
161 bf4daac9 Guido Trotter
    cmd_env = {}
162 bf4daac9 Guido Trotter
163 2557ff82 Guido Trotter
  if env is not None:
164 2557ff82 Guido Trotter
    cmd_env.update(env)
165 2557ff82 Guido Trotter
166 c803b052 Iustin Pop
  try:
167 c803b052 Iustin Pop
    if output is None:
168 c803b052 Iustin Pop
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
169 c803b052 Iustin Pop
    else:
170 c803b052 Iustin Pop
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
171 c803b052 Iustin Pop
      out = err = ""
172 c803b052 Iustin Pop
  except OSError, err:
173 c803b052 Iustin Pop
    if err.errno == errno.ENOENT:
174 c803b052 Iustin Pop
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
175 c803b052 Iustin Pop
                               (strcmd, err))
176 c803b052 Iustin Pop
    else:
177 c803b052 Iustin Pop
      raise
178 36117c2b Iustin Pop
179 36117c2b Iustin Pop
  if status >= 0:
180 36117c2b Iustin Pop
    exitcode = status
181 36117c2b Iustin Pop
    signal_ = None
182 36117c2b Iustin Pop
  else:
183 36117c2b Iustin Pop
    exitcode = None
184 36117c2b Iustin Pop
    signal_ = -status
185 36117c2b Iustin Pop
186 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
187 36117c2b Iustin Pop
188 ae59efea Michael Hanselmann
189 8797df43 Iustin Pop
def _RunCmdPipe(cmd, env, via_shell, cwd):
190 36117c2b Iustin Pop
  """Run a command and return its output.
191 36117c2b Iustin Pop

192 36117c2b Iustin Pop
  @type  cmd: string or list
193 36117c2b Iustin Pop
  @param cmd: Command to run
194 36117c2b Iustin Pop
  @type env: dict
195 36117c2b Iustin Pop
  @param env: The environment to use
196 36117c2b Iustin Pop
  @type via_shell: bool
197 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
198 8797df43 Iustin Pop
  @type cwd: string
199 8797df43 Iustin Pop
  @param cwd: the working directory for the program
200 36117c2b Iustin Pop
  @rtype: tuple
201 36117c2b Iustin Pop
  @return: (out, err, status)
202 36117c2b Iustin Pop

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

261 36117c2b Iustin Pop
  @type  cmd: string or list
262 36117c2b Iustin Pop
  @param cmd: Command to run
263 36117c2b Iustin Pop
  @type env: dict
264 36117c2b Iustin Pop
  @param env: The environment to use
265 36117c2b Iustin Pop
  @type via_shell: bool
266 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
267 36117c2b Iustin Pop
  @type output: str
268 36117c2b Iustin Pop
  @param output: the filename in which to save the output
269 8797df43 Iustin Pop
  @type cwd: string
270 8797df43 Iustin Pop
  @param cwd: the working directory for the program
271 36117c2b Iustin Pop
  @rtype: int
272 36117c2b Iustin Pop
  @return: the exit status
273 36117c2b Iustin Pop

274 36117c2b Iustin Pop
  """
275 36117c2b Iustin Pop
  fh = open(output, "a")
276 36117c2b Iustin Pop
  try:
277 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
278 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
279 36117c2b Iustin Pop
                             stdout=fh,
280 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
281 8797df43 Iustin Pop
                             close_fds=True, env=env,
282 8797df43 Iustin Pop
                             cwd=cwd)
283 36117c2b Iustin Pop
284 36117c2b Iustin Pop
    child.stdin.close()
285 36117c2b Iustin Pop
    status = child.wait()
286 36117c2b Iustin Pop
  finally:
287 36117c2b Iustin Pop
    fh.close()
288 36117c2b Iustin Pop
  return status
289 a8083063 Iustin Pop
290 a8083063 Iustin Pop
291 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
292 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
293 6bb65e3a Guido Trotter

294 6bb65e3a Guido Trotter
  @type dir_name: string
295 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
296 6bb65e3a Guido Trotter
  @type env: dict
297 6bb65e3a Guido Trotter
  @param env: The environment to use
298 6bb65e3a Guido Trotter
  @type reset_env: boolean
299 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
300 6bb65e3a Guido Trotter
  @rtype: list of tuples
301 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
302 6bb65e3a Guido Trotter

303 6bb65e3a Guido Trotter
  """
304 6bb65e3a Guido Trotter
  rr = []
305 6bb65e3a Guido Trotter
306 6bb65e3a Guido Trotter
  try:
307 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
308 6bb65e3a Guido Trotter
  except OSError, err:
309 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
310 6bb65e3a Guido Trotter
    return rr
311 6bb65e3a Guido Trotter
312 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
313 6bb65e3a Guido Trotter
    fname = os.path.join(dir_name, relname)
314 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
315 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
316 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
317 6bb65e3a Guido Trotter
    else:
318 6bb65e3a Guido Trotter
      try:
319 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
320 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
321 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
322 6bb65e3a Guido Trotter
      else:
323 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
324 6bb65e3a Guido Trotter
325 6bb65e3a Guido Trotter
  return rr
326 6bb65e3a Guido Trotter
327 6bb65e3a Guido Trotter
328 a8083063 Iustin Pop
def RemoveFile(filename):
329 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
330 a8083063 Iustin Pop

331 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
332 a8083063 Iustin Pop
  errors are passed.
333 a8083063 Iustin Pop

334 58885d79 Iustin Pop
  @type filename: str
335 58885d79 Iustin Pop
  @param filename: the file to be removed
336 58885d79 Iustin Pop

337 a8083063 Iustin Pop
  """
338 a8083063 Iustin Pop
  try:
339 a8083063 Iustin Pop
    os.unlink(filename)
340 a8083063 Iustin Pop
  except OSError, err:
341 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
342 a8083063 Iustin Pop
      raise
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
345 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
346 6e797216 Michael Hanselmann
  """Renames a file.
347 6e797216 Michael Hanselmann

348 6e797216 Michael Hanselmann
  @type old: string
349 6e797216 Michael Hanselmann
  @param old: Original path
350 6e797216 Michael Hanselmann
  @type new: string
351 6e797216 Michael Hanselmann
  @param new: New path
352 6e797216 Michael Hanselmann
  @type mkdir: bool
353 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
354 6e797216 Michael Hanselmann
  @type mkdir_mode: int
355 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
356 6e797216 Michael Hanselmann

357 6e797216 Michael Hanselmann
  """
358 6e797216 Michael Hanselmann
  try:
359 6e797216 Michael Hanselmann
    return os.rename(old, new)
360 6e797216 Michael Hanselmann
  except OSError, err:
361 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
362 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
363 6e797216 Michael Hanselmann
    # as efficient.
364 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
365 6e797216 Michael Hanselmann
      # Create directory and try again
366 a426508d Michael Hanselmann
      dirname = os.path.dirname(new)
367 a426508d Michael Hanselmann
      try:
368 a426508d Michael Hanselmann
        os.makedirs(dirname, mode=mkdir_mode)
369 a426508d Michael Hanselmann
      except OSError, err:
370 a426508d Michael Hanselmann
        # Ignore EEXIST. This is only handled in os.makedirs as included in
371 a426508d Michael Hanselmann
        # Python 2.5 and above.
372 a426508d Michael Hanselmann
        if err.errno != errno.EEXIST or not os.path.exists(dirname):
373 a426508d Michael Hanselmann
          raise
374 a426508d Michael Hanselmann
375 6e797216 Michael Hanselmann
      return os.rename(old, new)
376 a426508d Michael Hanselmann
377 6e797216 Michael Hanselmann
    raise
378 6e797216 Michael Hanselmann
379 6e797216 Michael Hanselmann
380 055f822b Michael Hanselmann
def ResetTempfileModule():
381 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
382 055f822b Michael Hanselmann

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

389 055f822b Michael Hanselmann
  """
390 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
391 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
392 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
393 055f822b Michael Hanselmann
    try:
394 055f822b Michael Hanselmann
      # Reset random name generator
395 055f822b Michael Hanselmann
      tempfile._name_sequence = None
396 055f822b Michael Hanselmann
    finally:
397 055f822b Michael Hanselmann
      tempfile._once_lock.release()
398 055f822b Michael Hanselmann
  else:
399 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
400 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
401 055f822b Michael Hanselmann
402 055f822b Michael Hanselmann
403 a8083063 Iustin Pop
def _FingerprintFile(filename):
404 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
405 a8083063 Iustin Pop

406 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
407 a8083063 Iustin Pop
  instead.
408 a8083063 Iustin Pop

409 58885d79 Iustin Pop
  @type filename: str
410 58885d79 Iustin Pop
  @param filename: the filename to checksum
411 58885d79 Iustin Pop
  @rtype: str
412 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
413 58885d79 Iustin Pop
      of the file
414 a8083063 Iustin Pop

415 a8083063 Iustin Pop
  """
416 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
417 a8083063 Iustin Pop
    return None
418 a8083063 Iustin Pop
419 a8083063 Iustin Pop
  f = open(filename)
420 a8083063 Iustin Pop
421 7ffe8fba Carlos Valiente
  fp = sha1()
422 a8083063 Iustin Pop
  while True:
423 a8083063 Iustin Pop
    data = f.read(4096)
424 a8083063 Iustin Pop
    if not data:
425 a8083063 Iustin Pop
      break
426 a8083063 Iustin Pop
427 a8083063 Iustin Pop
    fp.update(data)
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
  return fp.hexdigest()
430 a8083063 Iustin Pop
431 a8083063 Iustin Pop
432 a8083063 Iustin Pop
def FingerprintFiles(files):
433 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
434 a8083063 Iustin Pop

435 58885d79 Iustin Pop
  @type files: list
436 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
437 58885d79 Iustin Pop
  @rtype: dict
438 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
439 58885d79 Iustin Pop
      existing files
440 a8083063 Iustin Pop

441 a8083063 Iustin Pop
  """
442 a8083063 Iustin Pop
  ret = {}
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
  for filename in files:
445 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
446 a8083063 Iustin Pop
    if cksum:
447 a8083063 Iustin Pop
      ret[filename] = cksum
448 a8083063 Iustin Pop
449 a8083063 Iustin Pop
  return ret
450 a8083063 Iustin Pop
451 a8083063 Iustin Pop
452 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
453 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
454 a5728081 Guido Trotter

455 a5728081 Guido Trotter
  @type target: dict
456 a5728081 Guido Trotter
  @param target: the dict to update
457 a5728081 Guido Trotter
  @type key_types: dict
458 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
459 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
460 a5728081 Guido Trotter
  @type allowed_values: list
461 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
462 a5728081 Guido Trotter

463 a5728081 Guido Trotter
  """
464 a5728081 Guido Trotter
  if allowed_values is None:
465 a5728081 Guido Trotter
    allowed_values = []
466 a5728081 Guido Trotter
467 8b46606c Guido Trotter
  if not isinstance(target, dict):
468 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
469 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
470 8b46606c Guido Trotter
471 a5728081 Guido Trotter
  for key in target:
472 a5728081 Guido Trotter
    if key not in key_types:
473 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
474 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
475 a5728081 Guido Trotter
476 a5728081 Guido Trotter
    if target[key] in allowed_values:
477 a5728081 Guido Trotter
      continue
478 a5728081 Guido Trotter
479 29921401 Iustin Pop
    ktype = key_types[key]
480 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
481 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
482 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
483 a5728081 Guido Trotter
484 29921401 Iustin Pop
    if ktype == constants.VTYPE_STRING:
485 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
486 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
487 a5728081 Guido Trotter
          target[key] = ''
488 a5728081 Guido Trotter
        else:
489 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
490 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
491 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
492 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
493 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
494 a5728081 Guido Trotter
          target[key] = False
495 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
496 a5728081 Guido Trotter
          target[key] = True
497 a5728081 Guido Trotter
        else:
498 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
499 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
500 a5728081 Guido Trotter
      elif target[key]:
501 a5728081 Guido Trotter
        target[key] = True
502 a5728081 Guido Trotter
      else:
503 a5728081 Guido Trotter
        target[key] = False
504 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
505 a5728081 Guido Trotter
      try:
506 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
507 a5728081 Guido Trotter
      except errors.UnitParseError, err:
508 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
509 a5728081 Guido Trotter
              (key, target[key], err)
510 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
511 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
512 a5728081 Guido Trotter
      try:
513 a5728081 Guido Trotter
        target[key] = int(target[key])
514 a5728081 Guido Trotter
      except (ValueError, TypeError):
515 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
516 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
517 a5728081 Guido Trotter
518 a5728081 Guido Trotter
519 a8083063 Iustin Pop
def IsProcessAlive(pid):
520 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
521 a8083063 Iustin Pop

522 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
523 44bf25ff Iustin Pop
      will be returned as alive
524 58885d79 Iustin Pop
  @type pid: int
525 58885d79 Iustin Pop
  @param pid: the process ID to check
526 58885d79 Iustin Pop
  @rtype: boolean
527 58885d79 Iustin Pop
  @return: True if the process exists
528 a8083063 Iustin Pop

529 a8083063 Iustin Pop
  """
530 d9f311d7 Iustin Pop
  if pid <= 0:
531 d9f311d7 Iustin Pop
    return False
532 d9f311d7 Iustin Pop
533 a8083063 Iustin Pop
  try:
534 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
535 44bf25ff Iustin Pop
    return True
536 44bf25ff Iustin Pop
  except EnvironmentError, err:
537 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
538 a8083063 Iustin Pop
      return False
539 44bf25ff Iustin Pop
    raise
540 a8083063 Iustin Pop
541 a8083063 Iustin Pop
542 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
543 58885d79 Iustin Pop
  """Read a pid from a file.
544 fee80e90 Guido Trotter

545 58885d79 Iustin Pop
  @type  pidfile: string
546 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
547 58885d79 Iustin Pop
  @rtype: int
548 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
549 d9f311d7 Iustin Pop
           otherwise 0
550 fee80e90 Guido Trotter

551 fee80e90 Guido Trotter
  """
552 fee80e90 Guido Trotter
  try:
553 13998ef2 Michael Hanselmann
    raw_data = ReadFile(pidfile)
554 d9f311d7 Iustin Pop
  except EnvironmentError, err:
555 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
556 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
557 d9f311d7 Iustin Pop
    return 0
558 fee80e90 Guido Trotter
559 fee80e90 Guido Trotter
  try:
560 13998ef2 Michael Hanselmann
    pid = int(raw_data)
561 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
562 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
563 d9f311d7 Iustin Pop
    return 0
564 fee80e90 Guido Trotter
565 d9f311d7 Iustin Pop
  return pid
566 fee80e90 Guido Trotter
567 fee80e90 Guido Trotter
568 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
569 a8083063 Iustin Pop
  """Try to match a name against a list.
570 a8083063 Iustin Pop

571 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
572 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
573 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
574 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
575 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
576 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
577 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
578 a8083063 Iustin Pop

579 58885d79 Iustin Pop
  @type key: str
580 58885d79 Iustin Pop
  @param key: the name to be searched
581 58885d79 Iustin Pop
  @type name_list: list
582 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
583 256eb94b Guido Trotter
  @type case_sensitive: boolean
584 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
585 a8083063 Iustin Pop

586 58885d79 Iustin Pop
  @rtype: None or str
587 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
588 58885d79 Iustin Pop
      otherwise the element from the list which matches
589 a8083063 Iustin Pop

590 a8083063 Iustin Pop
  """
591 3a541d90 Iustin Pop
  if key in name_list:
592 3a541d90 Iustin Pop
    return key
593 256eb94b Guido Trotter
594 256eb94b Guido Trotter
  re_flags = 0
595 256eb94b Guido Trotter
  if not case_sensitive:
596 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
597 099c52ad Iustin Pop
    key = key.upper()
598 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
599 256eb94b Guido Trotter
  names_filtered = []
600 256eb94b Guido Trotter
  string_matches = []
601 256eb94b Guido Trotter
  for name in name_list:
602 256eb94b Guido Trotter
    if mo.match(name) is not None:
603 256eb94b Guido Trotter
      names_filtered.append(name)
604 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
605 256eb94b Guido Trotter
        string_matches.append(name)
606 256eb94b Guido Trotter
607 256eb94b Guido Trotter
  if len(string_matches) == 1:
608 256eb94b Guido Trotter
    return string_matches[0]
609 256eb94b Guido Trotter
  if len(names_filtered) == 1:
610 256eb94b Guido Trotter
    return names_filtered[0]
611 256eb94b Guido Trotter
  return None
612 a8083063 Iustin Pop
613 a8083063 Iustin Pop
614 bcf043c9 Iustin Pop
class HostInfo:
615 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
616 bcf043c9 Iustin Pop

617 bcf043c9 Iustin Pop
  """
618 89e1fc26 Iustin Pop
  def __init__(self, name=None):
619 bcf043c9 Iustin Pop
    """Initialize the host name object.
620 bcf043c9 Iustin Pop

621 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
622 89e1fc26 Iustin Pop
    name.
623 bcf043c9 Iustin Pop

624 bcf043c9 Iustin Pop
    """
625 89e1fc26 Iustin Pop
    if name is None:
626 89e1fc26 Iustin Pop
      name = self.SysName()
627 89e1fc26 Iustin Pop
628 89e1fc26 Iustin Pop
    self.query = name
629 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
630 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
631 bcf043c9 Iustin Pop
632 c8a0948f Michael Hanselmann
  def ShortName(self):
633 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
634 c8a0948f Michael Hanselmann

635 c8a0948f Michael Hanselmann
    """
636 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
637 c8a0948f Michael Hanselmann
638 89e1fc26 Iustin Pop
  @staticmethod
639 89e1fc26 Iustin Pop
  def SysName():
640 89e1fc26 Iustin Pop
    """Return the current system's name.
641 bcf043c9 Iustin Pop

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

644 89e1fc26 Iustin Pop
    """
645 89e1fc26 Iustin Pop
    return socket.gethostname()
646 a8083063 Iustin Pop
647 89e1fc26 Iustin Pop
  @staticmethod
648 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
649 89e1fc26 Iustin Pop
    """Look up hostname
650 a8083063 Iustin Pop

651 58885d79 Iustin Pop
    @type hostname: str
652 58885d79 Iustin Pop
    @param hostname: hostname to look up
653 89e1fc26 Iustin Pop

654 58885d79 Iustin Pop
    @rtype: tuple
655 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
656 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
657 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
658 89e1fc26 Iustin Pop

659 89e1fc26 Iustin Pop
    """
660 89e1fc26 Iustin Pop
    try:
661 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
662 89e1fc26 Iustin Pop
    except socket.gaierror, err:
663 89e1fc26 Iustin Pop
      # hostname not found in DNS
664 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
665 a8083063 Iustin Pop
666 89e1fc26 Iustin Pop
    return result
667 a8083063 Iustin Pop
668 a8083063 Iustin Pop
669 104f4ca1 Iustin Pop
def GetHostInfo(name=None):
670 104f4ca1 Iustin Pop
  """Lookup host name and raise an OpPrereqError for failures"""
671 104f4ca1 Iustin Pop
672 104f4ca1 Iustin Pop
  try:
673 104f4ca1 Iustin Pop
    return HostInfo(name)
674 104f4ca1 Iustin Pop
  except errors.ResolverError, err:
675 104f4ca1 Iustin Pop
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
676 104f4ca1 Iustin Pop
                               (err[0], err[2]), errors.ECODE_RESOLVER)
677 104f4ca1 Iustin Pop
678 104f4ca1 Iustin Pop
679 a8083063 Iustin Pop
def ListVolumeGroups():
680 a8083063 Iustin Pop
  """List volume groups and their size
681 a8083063 Iustin Pop

682 58885d79 Iustin Pop
  @rtype: dict
683 58885d79 Iustin Pop
  @return:
684 58885d79 Iustin Pop
       Dictionary with keys volume name and values
685 58885d79 Iustin Pop
       the size of the volume
686 a8083063 Iustin Pop

687 a8083063 Iustin Pop
  """
688 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
689 a8083063 Iustin Pop
  result = RunCmd(command)
690 a8083063 Iustin Pop
  retval = {}
691 a8083063 Iustin Pop
  if result.failed:
692 a8083063 Iustin Pop
    return retval
693 a8083063 Iustin Pop
694 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
695 a8083063 Iustin Pop
    try:
696 a8083063 Iustin Pop
      name, size = line.split()
697 a8083063 Iustin Pop
      size = int(float(size))
698 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
699 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
700 a8083063 Iustin Pop
      continue
701 a8083063 Iustin Pop
702 a8083063 Iustin Pop
    retval[name] = size
703 a8083063 Iustin Pop
704 a8083063 Iustin Pop
  return retval
705 a8083063 Iustin Pop
706 a8083063 Iustin Pop
707 a8083063 Iustin Pop
def BridgeExists(bridge):
708 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
709 a8083063 Iustin Pop

710 58885d79 Iustin Pop
  @type bridge: str
711 58885d79 Iustin Pop
  @param bridge: the bridge name to check
712 58885d79 Iustin Pop
  @rtype: boolean
713 58885d79 Iustin Pop
  @return: True if it does
714 a8083063 Iustin Pop

715 a8083063 Iustin Pop
  """
716 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
717 a8083063 Iustin Pop
718 a8083063 Iustin Pop
719 a8083063 Iustin Pop
def NiceSort(name_list):
720 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
721 a8083063 Iustin Pop

722 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
723 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
724 58885d79 Iustin Pop
  'a11']}.
725 a8083063 Iustin Pop

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

730 58885d79 Iustin Pop
  @type name_list: list
731 58885d79 Iustin Pop
  @param name_list: the names to be sorted
732 58885d79 Iustin Pop
  @rtype: list
733 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
734 a8083063 Iustin Pop

735 a8083063 Iustin Pop
  """
736 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
737 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
738 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
739 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
740 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
741 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
742 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
743 a8083063 Iustin Pop
  def _TryInt(val):
744 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
745 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
746 a8083063 Iustin Pop
      return val
747 a8083063 Iustin Pop
    rval = int(val)
748 a8083063 Iustin Pop
    return rval
749 a8083063 Iustin Pop
750 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
751 a8083063 Iustin Pop
             for name in name_list]
752 a8083063 Iustin Pop
  to_sort.sort()
753 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
754 a8083063 Iustin Pop
755 a8083063 Iustin Pop
756 a8083063 Iustin Pop
def TryConvert(fn, val):
757 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
758 a8083063 Iustin Pop

759 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
760 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
761 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
762 58885d79 Iustin Pop
  exceptions are propagated to the caller.
763 58885d79 Iustin Pop

764 58885d79 Iustin Pop
  @type fn: callable
765 58885d79 Iustin Pop
  @param fn: function to apply to the value
766 58885d79 Iustin Pop
  @param val: the value to be converted
767 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
768 58885d79 Iustin Pop
      otherwise the original value.
769 a8083063 Iustin Pop

770 a8083063 Iustin Pop
  """
771 a8083063 Iustin Pop
  try:
772 a8083063 Iustin Pop
    nv = fn(val)
773 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
774 a8083063 Iustin Pop
    nv = val
775 a8083063 Iustin Pop
  return nv
776 a8083063 Iustin Pop
777 a8083063 Iustin Pop
778 a8083063 Iustin Pop
def IsValidIP(ip):
779 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
780 a8083063 Iustin Pop

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

784 58885d79 Iustin Pop
  @type ip: str
785 58885d79 Iustin Pop
  @param ip: the address to be checked
786 58885d79 Iustin Pop
  @rtype: a regular expression match object
787 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
788 58885d79 Iustin Pop
      address is not valid
789 a8083063 Iustin Pop

790 a8083063 Iustin Pop
  """
791 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
792 58885d79 Iustin Pop
  #TODO: convert and return only boolean
793 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
794 a8083063 Iustin Pop
795 a8083063 Iustin Pop
796 a8083063 Iustin Pop
def IsValidShellParam(word):
797 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
798 a8083063 Iustin Pop

799 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
800 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
801 a8083063 Iustin Pop
  the actual command.
802 a8083063 Iustin Pop

803 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
804 a8083063 Iustin Pop
  side.
805 a8083063 Iustin Pop

806 58885d79 Iustin Pop
  @type word: str
807 58885d79 Iustin Pop
  @param word: the word to check
808 58885d79 Iustin Pop
  @rtype: boolean
809 58885d79 Iustin Pop
  @return: True if the word is 'safe'
810 58885d79 Iustin Pop

811 a8083063 Iustin Pop
  """
812 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
813 a8083063 Iustin Pop
814 a8083063 Iustin Pop
815 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
816 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
817 a8083063 Iustin Pop

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

823 58885d79 Iustin Pop
  @type template: str
824 58885d79 Iustin Pop
  @param template: the string holding the template for the
825 58885d79 Iustin Pop
      string formatting
826 58885d79 Iustin Pop
  @rtype: str
827 58885d79 Iustin Pop
  @return: the expanded command line
828 58885d79 Iustin Pop

829 a8083063 Iustin Pop
  """
830 a8083063 Iustin Pop
  for word in args:
831 a8083063 Iustin Pop
    if not IsValidShellParam(word):
832 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
833 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
834 a8083063 Iustin Pop
  return template % args
835 a8083063 Iustin Pop
836 a8083063 Iustin Pop
837 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
838 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
839 a8083063 Iustin Pop

840 58885d79 Iustin Pop
  @type value: int
841 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
842 9fbfbb7b Iustin Pop
  @type units: char
843 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
844 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
845 9fbfbb7b Iustin Pop
      - 'm' for MiBs
846 9fbfbb7b Iustin Pop
      - 'g' for GiBs
847 9fbfbb7b Iustin Pop
      - 't' for TiBs
848 58885d79 Iustin Pop
  @rtype: str
849 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
850 a8083063 Iustin Pop

851 a8083063 Iustin Pop
  """
852 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
853 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
854 a8083063 Iustin Pop
855 9fbfbb7b Iustin Pop
  suffix = ''
856 9fbfbb7b Iustin Pop
857 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
858 9fbfbb7b Iustin Pop
    if units == 'h':
859 9fbfbb7b Iustin Pop
      suffix = 'M'
860 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
861 9fbfbb7b Iustin Pop
862 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
863 9fbfbb7b Iustin Pop
    if units == 'h':
864 9fbfbb7b Iustin Pop
      suffix = 'G'
865 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
866 a8083063 Iustin Pop
867 a8083063 Iustin Pop
  else:
868 9fbfbb7b Iustin Pop
    if units == 'h':
869 9fbfbb7b Iustin Pop
      suffix = 'T'
870 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
871 a8083063 Iustin Pop
872 a8083063 Iustin Pop
873 a8083063 Iustin Pop
def ParseUnit(input_string):
874 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
875 a8083063 Iustin Pop

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

880 a8083063 Iustin Pop
  """
881 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
882 a8083063 Iustin Pop
  if not m:
883 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
884 a8083063 Iustin Pop
885 a8083063 Iustin Pop
  value = float(m.groups()[0])
886 a8083063 Iustin Pop
887 a8083063 Iustin Pop
  unit = m.groups()[1]
888 a8083063 Iustin Pop
  if unit:
889 a8083063 Iustin Pop
    lcunit = unit.lower()
890 a8083063 Iustin Pop
  else:
891 a8083063 Iustin Pop
    lcunit = 'm'
892 a8083063 Iustin Pop
893 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
894 a8083063 Iustin Pop
    # Value already in MiB
895 a8083063 Iustin Pop
    pass
896 a8083063 Iustin Pop
897 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
898 a8083063 Iustin Pop
    value *= 1024
899 a8083063 Iustin Pop
900 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
901 a8083063 Iustin Pop
    value *= 1024 * 1024
902 a8083063 Iustin Pop
903 a8083063 Iustin Pop
  else:
904 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
905 a8083063 Iustin Pop
906 a8083063 Iustin Pop
  # Make sure we round up
907 a8083063 Iustin Pop
  if int(value) < value:
908 a8083063 Iustin Pop
    value += 1
909 a8083063 Iustin Pop
910 a8083063 Iustin Pop
  # Round up to the next multiple of 4
911 a8083063 Iustin Pop
  value = int(value)
912 a8083063 Iustin Pop
  if value % 4:
913 a8083063 Iustin Pop
    value += 4 - value % 4
914 a8083063 Iustin Pop
915 a8083063 Iustin Pop
  return value
916 a8083063 Iustin Pop
917 a8083063 Iustin Pop
918 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
919 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
920 a8083063 Iustin Pop

921 58885d79 Iustin Pop
  @type file_name: str
922 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
923 58885d79 Iustin Pop
  @type key: str
924 58885d79 Iustin Pop
  @param key: string containing key
925 58885d79 Iustin Pop

926 a8083063 Iustin Pop
  """
927 a8083063 Iustin Pop
  key_fields = key.split()
928 a8083063 Iustin Pop
929 a8083063 Iustin Pop
  f = open(file_name, 'a+')
930 a8083063 Iustin Pop
  try:
931 a8083063 Iustin Pop
    nl = True
932 a8083063 Iustin Pop
    for line in f:
933 a8083063 Iustin Pop
      # Ignore whitespace changes
934 a8083063 Iustin Pop
      if line.split() == key_fields:
935 a8083063 Iustin Pop
        break
936 a8083063 Iustin Pop
      nl = line.endswith('\n')
937 a8083063 Iustin Pop
    else:
938 a8083063 Iustin Pop
      if not nl:
939 a8083063 Iustin Pop
        f.write("\n")
940 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
941 a8083063 Iustin Pop
      f.write("\n")
942 a8083063 Iustin Pop
      f.flush()
943 a8083063 Iustin Pop
  finally:
944 a8083063 Iustin Pop
    f.close()
945 a8083063 Iustin Pop
946 a8083063 Iustin Pop
947 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
948 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
949 a8083063 Iustin Pop

950 58885d79 Iustin Pop
  @type file_name: str
951 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
952 58885d79 Iustin Pop
  @type key: str
953 58885d79 Iustin Pop
  @param key: string containing key
954 58885d79 Iustin Pop

955 a8083063 Iustin Pop
  """
956 a8083063 Iustin Pop
  key_fields = key.split()
957 a8083063 Iustin Pop
958 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
959 a8083063 Iustin Pop
  try:
960 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
961 a8083063 Iustin Pop
    try:
962 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
963 59f82e3f Michael Hanselmann
      try:
964 59f82e3f Michael Hanselmann
        for line in f:
965 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
966 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
967 59f82e3f Michael Hanselmann
            out.write(line)
968 899d2a81 Michael Hanselmann
969 899d2a81 Michael Hanselmann
        out.flush()
970 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
971 899d2a81 Michael Hanselmann
      finally:
972 899d2a81 Michael Hanselmann
        f.close()
973 899d2a81 Michael Hanselmann
    finally:
974 899d2a81 Michael Hanselmann
      out.close()
975 899d2a81 Michael Hanselmann
  except:
976 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
977 899d2a81 Michael Hanselmann
    raise
978 899d2a81 Michael Hanselmann
979 899d2a81 Michael Hanselmann
980 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
981 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
982 899d2a81 Michael Hanselmann

983 58885d79 Iustin Pop
  @type file_name: str
984 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
985 58885d79 Iustin Pop
  @type ip: str
986 58885d79 Iustin Pop
  @param ip: the IP address
987 58885d79 Iustin Pop
  @type hostname: str
988 58885d79 Iustin Pop
  @param hostname: the hostname to be added
989 58885d79 Iustin Pop
  @type aliases: list
990 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
991 58885d79 Iustin Pop

992 899d2a81 Michael Hanselmann
  """
993 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
994 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
995 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
996 7fbb1f65 Michael Hanselmann
997 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
998 899d2a81 Michael Hanselmann
  try:
999 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1000 9440aeab Michael Hanselmann
    try:
1001 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1002 9440aeab Michael Hanselmann
      try:
1003 9440aeab Michael Hanselmann
        for line in f:
1004 9440aeab Michael Hanselmann
          fields = line.split()
1005 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1006 9440aeab Michael Hanselmann
            continue
1007 9440aeab Michael Hanselmann
          out.write(line)
1008 9440aeab Michael Hanselmann
1009 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1010 9440aeab Michael Hanselmann
        if aliases:
1011 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1012 9440aeab Michael Hanselmann
        out.write('\n')
1013 9440aeab Michael Hanselmann
1014 9440aeab Michael Hanselmann
        out.flush()
1015 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1016 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1017 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1018 9440aeab Michael Hanselmann
      finally:
1019 9440aeab Michael Hanselmann
        f.close()
1020 9440aeab Michael Hanselmann
    finally:
1021 9440aeab Michael Hanselmann
      out.close()
1022 9440aeab Michael Hanselmann
  except:
1023 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1024 9440aeab Michael Hanselmann
    raise
1025 899d2a81 Michael Hanselmann
1026 899d2a81 Michael Hanselmann
1027 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1028 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1029 d9c02ca6 Michael Hanselmann

1030 58885d79 Iustin Pop
  @type hostname: str
1031 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1032 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1033 58885d79 Iustin Pop

1034 d9c02ca6 Michael Hanselmann
  """
1035 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1036 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1037 d9c02ca6 Michael Hanselmann
1038 d9c02ca6 Michael Hanselmann
1039 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1040 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1041 899d2a81 Michael Hanselmann

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

1044 58885d79 Iustin Pop
  @type file_name: str
1045 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1046 58885d79 Iustin Pop
  @type hostname: str
1047 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1048 58885d79 Iustin Pop

1049 899d2a81 Michael Hanselmann
  """
1050 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1051 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1052 899d2a81 Michael Hanselmann
  try:
1053 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
1054 899d2a81 Michael Hanselmann
    try:
1055 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
1056 899d2a81 Michael Hanselmann
      try:
1057 899d2a81 Michael Hanselmann
        for line in f:
1058 899d2a81 Michael Hanselmann
          fields = line.split()
1059 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
1060 899d2a81 Michael Hanselmann
            names = fields[1:]
1061 899d2a81 Michael Hanselmann
            if hostname in names:
1062 899d2a81 Michael Hanselmann
              while hostname in names:
1063 899d2a81 Michael Hanselmann
                names.remove(hostname)
1064 899d2a81 Michael Hanselmann
              if names:
1065 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1066 899d2a81 Michael Hanselmann
              continue
1067 899d2a81 Michael Hanselmann
1068 899d2a81 Michael Hanselmann
          out.write(line)
1069 59f82e3f Michael Hanselmann
1070 59f82e3f Michael Hanselmann
        out.flush()
1071 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1072 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1073 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1074 59f82e3f Michael Hanselmann
      finally:
1075 59f82e3f Michael Hanselmann
        f.close()
1076 a8083063 Iustin Pop
    finally:
1077 59f82e3f Michael Hanselmann
      out.close()
1078 59f82e3f Michael Hanselmann
  except:
1079 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1080 59f82e3f Michael Hanselmann
    raise
1081 a8083063 Iustin Pop
1082 a8083063 Iustin Pop
1083 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1084 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1085 d9c02ca6 Michael Hanselmann

1086 58885d79 Iustin Pop
  @type hostname: str
1087 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1088 58885d79 Iustin Pop
      full and shot name will be removed from
1089 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1090 58885d79 Iustin Pop

1091 d9c02ca6 Michael Hanselmann
  """
1092 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1093 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1094 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1095 d9c02ca6 Michael Hanselmann
1096 d9c02ca6 Michael Hanselmann
1097 a8083063 Iustin Pop
def CreateBackup(file_name):
1098 a8083063 Iustin Pop
  """Creates a backup of a file.
1099 a8083063 Iustin Pop

1100 58885d79 Iustin Pop
  @type file_name: str
1101 58885d79 Iustin Pop
  @param file_name: file to be backed up
1102 58885d79 Iustin Pop
  @rtype: str
1103 58885d79 Iustin Pop
  @return: the path to the newly created backup
1104 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1105 a8083063 Iustin Pop

1106 a8083063 Iustin Pop
  """
1107 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1108 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1109 3ecf6786 Iustin Pop
                                file_name)
1110 a8083063 Iustin Pop
1111 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
1112 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1113 081b1e69 Michael Hanselmann
1114 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1115 081b1e69 Michael Hanselmann
  try:
1116 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1117 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1118 081b1e69 Michael Hanselmann
    try:
1119 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1120 081b1e69 Michael Hanselmann
    finally:
1121 081b1e69 Michael Hanselmann
      fdst.close()
1122 081b1e69 Michael Hanselmann
  finally:
1123 081b1e69 Michael Hanselmann
    fsrc.close()
1124 081b1e69 Michael Hanselmann
1125 a8083063 Iustin Pop
  return backup_name
1126 a8083063 Iustin Pop
1127 a8083063 Iustin Pop
1128 a8083063 Iustin Pop
def ShellQuote(value):
1129 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1130 3ecf6786 Iustin Pop

1131 58885d79 Iustin Pop
  @type value: str
1132 58885d79 Iustin Pop
  @param value: the argument to be quoted
1133 58885d79 Iustin Pop
  @rtype: str
1134 58885d79 Iustin Pop
  @return: the quoted value
1135 58885d79 Iustin Pop

1136 a8083063 Iustin Pop
  """
1137 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1138 a8083063 Iustin Pop
    return value
1139 a8083063 Iustin Pop
  else:
1140 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1141 a8083063 Iustin Pop
1142 a8083063 Iustin Pop
1143 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1144 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1145 58885d79 Iustin Pop

1146 58885d79 Iustin Pop
  @type args: list
1147 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1148 58885d79 Iustin Pop
  @rtype: str
1149 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1150 a8083063 Iustin Pop

1151 a8083063 Iustin Pop
  """
1152 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1153 88d14415 Michael Hanselmann
1154 88d14415 Michael Hanselmann
1155 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1156 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1157 2c30e9d7 Alexander Schreiber

1158 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1159 58885d79 Iustin Pop
  to it.
1160 58885d79 Iustin Pop

1161 58885d79 Iustin Pop
  @type target: str
1162 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1163 58885d79 Iustin Pop
  @type port: int
1164 58885d79 Iustin Pop
  @param port: the port to connect to
1165 58885d79 Iustin Pop
  @type timeout: int
1166 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1167 58885d79 Iustin Pop
  @type live_port_needed: boolean
1168 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1169 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1170 58885d79 Iustin Pop
  @type source: str or None
1171 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1172 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1173 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1174 2c30e9d7 Alexander Schreiber

1175 2c30e9d7 Alexander Schreiber
  """
1176 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1177 2c30e9d7 Alexander Schreiber
1178 0b5ad33e Iustin Pop
  success = False
1179 2c30e9d7 Alexander Schreiber
1180 b15d625f Iustin Pop
  if source is not None:
1181 b15d625f Iustin Pop
    try:
1182 b15d625f Iustin Pop
      sock.bind((source, 0))
1183 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1184 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1185 b15d625f Iustin Pop
        success = False
1186 2c30e9d7 Alexander Schreiber
1187 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1188 2c30e9d7 Alexander Schreiber
1189 2c30e9d7 Alexander Schreiber
  try:
1190 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1191 2c30e9d7 Alexander Schreiber
    sock.close()
1192 2c30e9d7 Alexander Schreiber
    success = True
1193 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1194 2c30e9d7 Alexander Schreiber
    success = False
1195 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1196 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1197 2c30e9d7 Alexander Schreiber
1198 2c30e9d7 Alexander Schreiber
  return success
1199 eedbda4b Michael Hanselmann
1200 eedbda4b Michael Hanselmann
1201 caad16e2 Iustin Pop
def OwnIpAddress(address):
1202 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1203 caad16e2 Iustin Pop

1204 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1205 caad16e2 Iustin Pop
  address.
1206 caad16e2 Iustin Pop

1207 caad16e2 Iustin Pop
  @type address: string
1208 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1209 caad16e2 Iustin Pop
  @rtype: bool
1210 58885d79 Iustin Pop
  @return: True if we own the address
1211 caad16e2 Iustin Pop

1212 caad16e2 Iustin Pop
  """
1213 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1214 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1215 caad16e2 Iustin Pop
1216 caad16e2 Iustin Pop
1217 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1218 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1219 58885d79 Iustin Pop

1220 58885d79 Iustin Pop
  @type path: str
1221 58885d79 Iustin Pop
  @param path: the directory to enumerate
1222 58885d79 Iustin Pop
  @rtype: list
1223 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1224 eedbda4b Michael Hanselmann

1225 eedbda4b Michael Hanselmann
  """
1226 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1227 f3299a07 Michael Hanselmann
  files.sort()
1228 f3299a07 Michael Hanselmann
  return files
1229 2f8b60b3 Iustin Pop
1230 2f8b60b3 Iustin Pop
1231 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1232 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1233 257f4c0a Iustin Pop

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

1238 2f8b60b3 Iustin Pop
  """
1239 2f8b60b3 Iustin Pop
  try:
1240 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1241 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1242 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1243 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1244 257f4c0a Iustin Pop
    else:
1245 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1246 257f4c0a Iustin Pop
                                   type(user))
1247 2f8b60b3 Iustin Pop
  except KeyError:
1248 2f8b60b3 Iustin Pop
    return default
1249 2f8b60b3 Iustin Pop
  return result.pw_dir
1250 59072e7e Michael Hanselmann
1251 59072e7e Michael Hanselmann
1252 24818e8f Michael Hanselmann
def NewUUID():
1253 59072e7e Michael Hanselmann
  """Returns a random UUID.
1254 59072e7e Michael Hanselmann

1255 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1256 58885d79 Iustin Pop
      filesystem.
1257 58885d79 Iustin Pop
  @rtype: str
1258 58885d79 Iustin Pop

1259 59072e7e Michael Hanselmann
  """
1260 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1261 087b34fe Iustin Pop
1262 087b34fe Iustin Pop
1263 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1264 33081d90 Iustin Pop
  """Generates a random secret.
1265 33081d90 Iustin Pop

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

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

1274 33081d90 Iustin Pop
  """
1275 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1276 33081d90 Iustin Pop
1277 33081d90 Iustin Pop
1278 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1279 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1280 9dae41ad Guido Trotter

1281 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1282 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1283 9dae41ad Guido Trotter

1284 9dae41ad Guido Trotter
  """
1285 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1286 9dae41ad Guido Trotter
    try:
1287 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1288 9dae41ad Guido Trotter
    except EnvironmentError, err:
1289 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1290 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1291 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1292 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1293 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1294 9dae41ad Guido Trotter
1295 9dae41ad Guido Trotter
1296 016308cb Iustin Pop
def ReadFile(file_name, size=-1):
1297 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1298 ca0aa6d0 Michael Hanselmann

1299 016308cb Iustin Pop
  @type size: int
1300 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1301 58885d79 Iustin Pop
  @rtype: str
1302 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1303 ca0aa6d0 Michael Hanselmann

1304 ca0aa6d0 Michael Hanselmann
  """
1305 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1306 ca0aa6d0 Michael Hanselmann
  try:
1307 016308cb Iustin Pop
    return f.read(size)
1308 ca0aa6d0 Michael Hanselmann
  finally:
1309 ca0aa6d0 Michael Hanselmann
    f.close()
1310 ca0aa6d0 Michael Hanselmann
1311 ca0aa6d0 Michael Hanselmann
1312 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1313 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1314 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1315 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1316 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1317 087b34fe Iustin Pop
  """(Over)write a file atomically.
1318 087b34fe Iustin Pop

1319 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1320 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1321 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1322 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1323 087b34fe Iustin Pop
  mtime/atime of the file.
1324 087b34fe Iustin Pop

1325 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1326 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1327 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1328 087b34fe Iustin Pop
  temporary file should be removed.
1329 087b34fe Iustin Pop

1330 58885d79 Iustin Pop
  @type file_name: str
1331 58885d79 Iustin Pop
  @param file_name: the target filename
1332 58885d79 Iustin Pop
  @type fn: callable
1333 58885d79 Iustin Pop
  @param fn: content writing function, called with
1334 58885d79 Iustin Pop
      file descriptor as parameter
1335 69efe319 Michael Hanselmann
  @type data: str
1336 58885d79 Iustin Pop
  @param data: contents of the file
1337 58885d79 Iustin Pop
  @type mode: int
1338 58885d79 Iustin Pop
  @param mode: file mode
1339 58885d79 Iustin Pop
  @type uid: int
1340 58885d79 Iustin Pop
  @param uid: the owner of the file
1341 58885d79 Iustin Pop
  @type gid: int
1342 58885d79 Iustin Pop
  @param gid: the group of the file
1343 58885d79 Iustin Pop
  @type atime: int
1344 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1345 58885d79 Iustin Pop
  @type mtime: int
1346 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1347 58885d79 Iustin Pop
  @type close: boolean
1348 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1349 58885d79 Iustin Pop
  @type prewrite: callable
1350 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1351 58885d79 Iustin Pop
  @type postwrite: callable
1352 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1353 58885d79 Iustin Pop

1354 58885d79 Iustin Pop
  @rtype: None or int
1355 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1356 58885d79 Iustin Pop
      otherwise the file descriptor
1357 58885d79 Iustin Pop

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

1360 087b34fe Iustin Pop
  """
1361 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1362 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1363 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1364 087b34fe Iustin Pop
1365 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1366 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1367 087b34fe Iustin Pop
1368 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1369 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1370 087b34fe Iustin Pop
                                 " set or None")
1371 087b34fe Iustin Pop
1372 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1373 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1374 087b34fe Iustin Pop
1375 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1376 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1377 81b7354c Iustin Pop
  do_remove = True
1378 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1379 087b34fe Iustin Pop
  # leaves it in place
1380 087b34fe Iustin Pop
  try:
1381 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1382 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1383 087b34fe Iustin Pop
    if mode:
1384 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1385 71714516 Michael Hanselmann
    if callable(prewrite):
1386 71714516 Michael Hanselmann
      prewrite(fd)
1387 087b34fe Iustin Pop
    if data is not None:
1388 087b34fe Iustin Pop
      os.write(fd, data)
1389 087b34fe Iustin Pop
    else:
1390 087b34fe Iustin Pop
      fn(fd)
1391 71714516 Michael Hanselmann
    if callable(postwrite):
1392 71714516 Michael Hanselmann
      postwrite(fd)
1393 087b34fe Iustin Pop
    os.fsync(fd)
1394 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1395 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1396 70f4497c Michael Hanselmann
    if not dry_run:
1397 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1398 81b7354c Iustin Pop
      do_remove = False
1399 087b34fe Iustin Pop
  finally:
1400 71714516 Michael Hanselmann
    if close:
1401 71714516 Michael Hanselmann
      os.close(fd)
1402 71714516 Michael Hanselmann
      result = None
1403 71714516 Michael Hanselmann
    else:
1404 71714516 Michael Hanselmann
      result = fd
1405 81b7354c Iustin Pop
    if do_remove:
1406 81b7354c Iustin Pop
      RemoveFile(new_name)
1407 78feb6fb Guido Trotter
1408 71714516 Michael Hanselmann
  return result
1409 71714516 Michael Hanselmann
1410 78feb6fb Guido Trotter
1411 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1412 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1413 7b4126b7 Iustin Pop

1414 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1415 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1416 7b4126b7 Iustin Pop
  value, the index will be returned.
1417 7b4126b7 Iustin Pop

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

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

1423 58885d79 Iustin Pop
  @type seq: sequence
1424 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1425 58885d79 Iustin Pop
  @type base: int
1426 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1427 58885d79 Iustin Pop
  @rtype: int
1428 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1429 7b4126b7 Iustin Pop

1430 7b4126b7 Iustin Pop
  """
1431 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1432 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1433 7b4126b7 Iustin Pop
    if elem > idx + base:
1434 7b4126b7 Iustin Pop
      # idx is not used
1435 7b4126b7 Iustin Pop
      return idx + base
1436 7b4126b7 Iustin Pop
  return None
1437 7b4126b7 Iustin Pop
1438 7b4126b7 Iustin Pop
1439 7260cfbe Iustin Pop
def all(seq, pred=bool): # pylint: disable-msg=W0622
1440 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1441 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilterfalse(pred, seq):
1442 78feb6fb Guido Trotter
    return False
1443 78feb6fb Guido Trotter
  return True
1444 78feb6fb Guido Trotter
1445 78feb6fb Guido Trotter
1446 7260cfbe Iustin Pop
def any(seq, pred=bool): # pylint: disable-msg=W0622
1447 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1448 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilter(pred, seq):
1449 78feb6fb Guido Trotter
    return True
1450 78feb6fb Guido Trotter
  return False
1451 f7414041 Michael Hanselmann
1452 f7414041 Michael Hanselmann
1453 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1454 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1455 f7414041 Michael Hanselmann

1456 f7414041 Michael Hanselmann
  Element order is preserved.
1457 58885d79 Iustin Pop

1458 58885d79 Iustin Pop
  @type seq: sequence
1459 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1460 58885d79 Iustin Pop
  @rtype: list
1461 58885d79 Iustin Pop
  @return: list of unique elements from seq
1462 58885d79 Iustin Pop

1463 f7414041 Michael Hanselmann
  """
1464 f7414041 Michael Hanselmann
  seen = set()
1465 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1466 1862d460 Alexander Schreiber
1467 1862d460 Alexander Schreiber
1468 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1469 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1470 1862d460 Alexander Schreiber

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

1474 58885d79 Iustin Pop
  @type mac: str
1475 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1476 82187135 Renรฉ Nussbaumer
  @rtype: str
1477 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1478 82187135 Renรฉ Nussbaumer

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

1481 1862d460 Alexander Schreiber
  """
1482 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1483 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1484 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1485 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1486 82187135 Renรฉ Nussbaumer
1487 82187135 Renรฉ Nussbaumer
  return mac.lower()
1488 06009e27 Iustin Pop
1489 06009e27 Iustin Pop
1490 06009e27 Iustin Pop
def TestDelay(duration):
1491 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1492 06009e27 Iustin Pop

1493 58885d79 Iustin Pop
  @type duration: float
1494 58885d79 Iustin Pop
  @param duration: the sleep duration
1495 58885d79 Iustin Pop
  @rtype: boolean
1496 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1497 58885d79 Iustin Pop

1498 06009e27 Iustin Pop
  """
1499 06009e27 Iustin Pop
  if duration < 0:
1500 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1501 06009e27 Iustin Pop
  time.sleep(duration)
1502 38ea42a1 Iustin Pop
  return True, None
1503 8f765069 Iustin Pop
1504 8f765069 Iustin Pop
1505 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1506 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1507 8f765069 Iustin Pop

1508 7d88772a Iustin Pop
  @type fd: int
1509 7d88772a Iustin Pop
  @param fd: the file descriptor
1510 7d88772a Iustin Pop
  @type retries: int
1511 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1512 7d88772a Iustin Pop
      other error than EBADF
1513 7d88772a Iustin Pop

1514 7d88772a Iustin Pop
  """
1515 7d88772a Iustin Pop
  try:
1516 7d88772a Iustin Pop
    os.close(fd)
1517 7d88772a Iustin Pop
  except OSError, err:
1518 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1519 7d88772a Iustin Pop
      if retries > 0:
1520 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1521 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1522 7d88772a Iustin Pop
    # ignore this and go on
1523 7d88772a Iustin Pop
1524 7d88772a Iustin Pop
1525 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1526 7d88772a Iustin Pop
  """Close file descriptors.
1527 7d88772a Iustin Pop

1528 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1529 7d88772a Iustin Pop
  stdin/out/err).
1530 8f765069 Iustin Pop

1531 58885d79 Iustin Pop
  @type noclose_fds: list or None
1532 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1533 58885d79 Iustin Pop
      that should not be closed
1534 58885d79 Iustin Pop

1535 8f765069 Iustin Pop
  """
1536 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1537 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1538 8f765069 Iustin Pop
    try:
1539 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1540 8f765069 Iustin Pop
      if MAXFD < 0:
1541 8f765069 Iustin Pop
        MAXFD = 1024
1542 8f765069 Iustin Pop
    except OSError:
1543 8f765069 Iustin Pop
      MAXFD = 1024
1544 8f765069 Iustin Pop
  else:
1545 8f765069 Iustin Pop
    MAXFD = 1024
1546 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1547 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1548 7d88772a Iustin Pop
    maxfd = MAXFD
1549 7d88772a Iustin Pop
1550 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1551 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1552 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1553 7d88772a Iustin Pop
      continue
1554 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1555 7d88772a Iustin Pop
1556 7d88772a Iustin Pop
1557 7d88772a Iustin Pop
def Daemonize(logfile):
1558 7d88772a Iustin Pop
  """Daemonize the current process.
1559 7d88772a Iustin Pop

1560 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1561 7d88772a Iustin Pop
  runs it in the background as a daemon.
1562 7d88772a Iustin Pop

1563 7d88772a Iustin Pop
  @type logfile: str
1564 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1565 7d88772a Iustin Pop
  @rtype: int
1566 5fcc718f Iustin Pop
  @return: the value zero
1567 7d88772a Iustin Pop

1568 7d88772a Iustin Pop
  """
1569 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1570 7260cfbe Iustin Pop
  # yes, we really want os._exit
1571 7d88772a Iustin Pop
  UMASK = 077
1572 7d88772a Iustin Pop
  WORKDIR = "/"
1573 8f765069 Iustin Pop
1574 8f765069 Iustin Pop
  # this might fail
1575 8f765069 Iustin Pop
  pid = os.fork()
1576 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1577 8f765069 Iustin Pop
    os.setsid()
1578 8f765069 Iustin Pop
    # this might fail
1579 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1580 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1581 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1582 8f765069 Iustin Pop
      os.umask(UMASK)
1583 8f765069 Iustin Pop
    else:
1584 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1585 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1586 8f765069 Iustin Pop
  else:
1587 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1588 8f765069 Iustin Pop
1589 7d88772a Iustin Pop
  for fd in range(3):
1590 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1591 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1592 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1593 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1594 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1595 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1596 7d88772a Iustin Pop
  os.dup2(1, 2)
1597 8f765069 Iustin Pop
  return 0
1598 57c177af Iustin Pop
1599 57c177af Iustin Pop
1600 53beffbb Iustin Pop
def DaemonPidFileName(name):
1601 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1602 58885d79 Iustin Pop

1603 58885d79 Iustin Pop
  @type name: str
1604 58885d79 Iustin Pop
  @param name: the daemon name
1605 58885d79 Iustin Pop
  @rtype: str
1606 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1607 58885d79 Iustin Pop
      daemon name
1608 b330ac0b Guido Trotter

1609 b330ac0b Guido Trotter
  """
1610 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1611 b330ac0b Guido Trotter
1612 b330ac0b Guido Trotter
1613 b330ac0b Guido Trotter
def WritePidFile(name):
1614 b330ac0b Guido Trotter
  """Write the current process pidfile.
1615 b330ac0b Guido Trotter

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

1618 58885d79 Iustin Pop
  @type name: str
1619 58885d79 Iustin Pop
  @param name: the daemon name to use
1620 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1621 58885d79 Iustin Pop
      points to a live process
1622 b330ac0b Guido Trotter

1623 b330ac0b Guido Trotter
  """
1624 b330ac0b Guido Trotter
  pid = os.getpid()
1625 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1626 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1627 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1628 b330ac0b Guido Trotter
1629 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1630 b330ac0b Guido Trotter
1631 b330ac0b Guido Trotter
1632 b330ac0b Guido Trotter
def RemovePidFile(name):
1633 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1634 b330ac0b Guido Trotter

1635 b330ac0b Guido Trotter
  Any errors are ignored.
1636 b330ac0b Guido Trotter

1637 58885d79 Iustin Pop
  @type name: str
1638 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1639 58885d79 Iustin Pop

1640 b330ac0b Guido Trotter
  """
1641 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1642 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1643 b330ac0b Guido Trotter
  try:
1644 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1645 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1646 b330ac0b Guido Trotter
    pass
1647 b330ac0b Guido Trotter
1648 b330ac0b Guido Trotter
1649 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1650 ff5251bc Iustin Pop
                waitpid=False):
1651 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1652 b2a1f511 Iustin Pop

1653 b2a1f511 Iustin Pop
  @type pid: int
1654 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1655 38206f3c Iustin Pop
  @type signal_: int
1656 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1657 b2a1f511 Iustin Pop
  @type timeout: int
1658 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1659 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1660 b2a1f511 Iustin Pop
                  will be done
1661 ff5251bc Iustin Pop
  @type waitpid: boolean
1662 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1663 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1664 ff5251bc Iustin Pop
      would remain as zombie
1665 b2a1f511 Iustin Pop

1666 b2a1f511 Iustin Pop
  """
1667 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1668 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1669 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1670 ff5251bc Iustin Pop
    if wait:
1671 ff5251bc Iustin Pop
      try:
1672 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1673 ff5251bc Iustin Pop
      except OSError:
1674 ff5251bc Iustin Pop
        pass
1675 ff5251bc Iustin Pop
1676 b2a1f511 Iustin Pop
  if pid <= 0:
1677 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1678 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1679 b2a1f511 Iustin Pop
1680 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1681 b2a1f511 Iustin Pop
    return
1682 31892b4c Michael Hanselmann
1683 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1684 31892b4c Michael Hanselmann
1685 b2a1f511 Iustin Pop
  if timeout <= 0:
1686 b2a1f511 Iustin Pop
    return
1687 7167159a Michael Hanselmann
1688 31892b4c Michael Hanselmann
  def _CheckProcess():
1689 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1690 31892b4c Michael Hanselmann
      return
1691 31892b4c Michael Hanselmann
1692 7167159a Michael Hanselmann
    try:
1693 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1694 7167159a Michael Hanselmann
    except OSError:
1695 31892b4c Michael Hanselmann
      raise RetryAgain()
1696 31892b4c Michael Hanselmann
1697 31892b4c Michael Hanselmann
    if result_pid > 0:
1698 31892b4c Michael Hanselmann
      return
1699 31892b4c Michael Hanselmann
1700 31892b4c Michael Hanselmann
    raise RetryAgain()
1701 31892b4c Michael Hanselmann
1702 31892b4c Michael Hanselmann
  try:
1703 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1704 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1705 31892b4c Michael Hanselmann
  except RetryTimeout:
1706 31892b4c Michael Hanselmann
    pass
1707 7167159a Michael Hanselmann
1708 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1709 7167159a Michael Hanselmann
    # Kill process if it's still alive
1710 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1711 b2a1f511 Iustin Pop
1712 b2a1f511 Iustin Pop
1713 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1714 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1715 57c177af Iustin Pop

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

1719 58885d79 Iustin Pop
  @type name: str
1720 58885d79 Iustin Pop
  @param name: the name to look for
1721 58885d79 Iustin Pop
  @type search_path: str
1722 58885d79 Iustin Pop
  @param search_path: location to start at
1723 58885d79 Iustin Pop
  @type test: callable
1724 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1725 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1726 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1727 58885d79 Iustin Pop
  @rtype: str or None
1728 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1729 57c177af Iustin Pop

1730 57c177af Iustin Pop
  """
1731 f95c81bf Iustin Pop
  # validate the filename mask
1732 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1733 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1734 f95c81bf Iustin Pop
                     name)
1735 f95c81bf Iustin Pop
    return None
1736 f95c81bf Iustin Pop
1737 57c177af Iustin Pop
  for dir_name in search_path:
1738 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1739 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1740 f95c81bf Iustin Pop
    # basename
1741 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1742 57c177af Iustin Pop
      return item_name
1743 57c177af Iustin Pop
  return None
1744 8d1a2a64 Michael Hanselmann
1745 8d1a2a64 Michael Hanselmann
1746 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1747 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1748 8d1a2a64 Michael Hanselmann

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

1752 58885d79 Iustin Pop
  @type vglist: dict
1753 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1754 58885d79 Iustin Pop
  @type vgname: str
1755 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1756 58885d79 Iustin Pop
  @type minsize: int
1757 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1758 58885d79 Iustin Pop
  @rtype: None or str
1759 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1760 8d1a2a64 Michael Hanselmann

1761 8d1a2a64 Michael Hanselmann
  """
1762 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1763 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1764 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1765 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1766 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1767 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1768 8d1a2a64 Michael Hanselmann
  return None
1769 7996a135 Iustin Pop
1770 7996a135 Iustin Pop
1771 45bc5e4a Michael Hanselmann
def SplitTime(value):
1772 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1773 739be818 Michael Hanselmann

1774 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1775 45bc5e4a Michael Hanselmann
  @type value: int or float
1776 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1777 739be818 Michael Hanselmann

1778 739be818 Michael Hanselmann
  """
1779 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1780 45bc5e4a Michael Hanselmann
1781 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1782 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1783 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1784 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1785 45bc5e4a Michael Hanselmann
1786 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1787 739be818 Michael Hanselmann
1788 739be818 Michael Hanselmann
1789 739be818 Michael Hanselmann
def MergeTime(timetuple):
1790 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1791 739be818 Michael Hanselmann

1792 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1793 739be818 Michael Hanselmann
  @type timetuple: tuple
1794 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1795 739be818 Michael Hanselmann

1796 739be818 Michael Hanselmann
  """
1797 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1798 739be818 Michael Hanselmann
1799 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1800 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1801 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1802 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1803 739be818 Michael Hanselmann
1804 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1805 739be818 Michael Hanselmann
1806 739be818 Michael Hanselmann
1807 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1808 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1809 4a8b186a Michael Hanselmann

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

1814 cd50653c Guido Trotter
  @type daemon_name: string
1815 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1816 58885d79 Iustin Pop
  @rtype: int
1817 58885d79 Iustin Pop

1818 4a8b186a Michael Hanselmann
  """
1819 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1820 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1821 cd50653c Guido Trotter
1822 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1823 4a8b186a Michael Hanselmann
  try:
1824 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1825 4a8b186a Michael Hanselmann
  except socket.error:
1826 cd50653c Guido Trotter
    port = default_port
1827 4a8b186a Michael Hanselmann
1828 4a8b186a Michael Hanselmann
  return port
1829 4a8b186a Michael Hanselmann
1830 4a8b186a Michael Hanselmann
1831 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
1832 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
1833 82d9caef Iustin Pop
  """Configures the logging module.
1834 82d9caef Iustin Pop

1835 58885d79 Iustin Pop
  @type logfile: str
1836 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1837 ea34193f Iustin Pop
  @type debug: integer
1838 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
1839 58885d79 Iustin Pop
      only those at C{INFO} and above level
1840 58885d79 Iustin Pop
  @type stderr_logging: boolean
1841 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1842 58885d79 Iustin Pop
  @type program: str
1843 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1844 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1845 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1846 551b6283 Iustin Pop
  @type syslog: string
1847 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
1848 551b6283 Iustin Pop
      - if no, syslog is not used
1849 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
1850 551b6283 Iustin Pop
      - if only, only syslog is used
1851 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1852 551b6283 Iustin Pop
      syslog/stderr logging is disabled
1853 58885d79 Iustin Pop

1854 82d9caef Iustin Pop
  """
1855 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1856 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
1857 d21d09d6 Iustin Pop
  if multithreaded:
1858 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1859 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
1860 82d9caef Iustin Pop
  if debug:
1861 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1862 551b6283 Iustin Pop
    # no debug info for syslog loggers
1863 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1864 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
1865 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
1866 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
1867 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1868 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
1869 82d9caef Iustin Pop
1870 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1871 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1872 82d9caef Iustin Pop
1873 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1874 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1875 7d88772a Iustin Pop
    handler.close()
1876 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1877 6346a9e5 Michael Hanselmann
1878 82d9caef Iustin Pop
  if stderr_logging:
1879 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1880 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1881 82d9caef Iustin Pop
    if debug:
1882 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1883 82d9caef Iustin Pop
    else:
1884 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1885 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1886 82d9caef Iustin Pop
1887 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
1888 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
1889 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
1890 551b6283 Iustin Pop
                                                    facility)
1891 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
1892 551b6283 Iustin Pop
    # Never enable debug over syslog
1893 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
1894 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
1895 551b6283 Iustin Pop
1896 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
1897 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
1898 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
1899 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
1900 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
1901 551b6283 Iustin Pop
    try:
1902 551b6283 Iustin Pop
      logfile_handler = logging.FileHandler(logfile)
1903 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
1904 551b6283 Iustin Pop
      if debug:
1905 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
1906 551b6283 Iustin Pop
      else:
1907 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
1908 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
1909 551b6283 Iustin Pop
    except EnvironmentError:
1910 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
1911 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
1912 551b6283 Iustin Pop
      else:
1913 551b6283 Iustin Pop
        # we need to re-raise the exception
1914 551b6283 Iustin Pop
        raise
1915 82d9caef Iustin Pop
1916 016d04b3 Michael Hanselmann
1917 da961187 Guido Trotter
def IsNormAbsPath(path):
1918 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
1919 da961187 Guido Trotter

1920 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1921 da961187 Guido Trotter

1922 da961187 Guido Trotter
  """
1923 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1924 82d9caef Iustin Pop
1925 016d04b3 Michael Hanselmann
1926 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1927 f65f63ef Iustin Pop
  """Return the last lines from a file.
1928 f65f63ef Iustin Pop

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

1933 f65f63ef Iustin Pop
  @param fname: the file name
1934 f65f63ef Iustin Pop
  @type lines: int
1935 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1936 f65f63ef Iustin Pop

1937 f65f63ef Iustin Pop
  """
1938 f65f63ef Iustin Pop
  fd = open(fname, "r")
1939 f65f63ef Iustin Pop
  try:
1940 f65f63ef Iustin Pop
    fd.seek(0, 2)
1941 f65f63ef Iustin Pop
    pos = fd.tell()
1942 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1943 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1944 f65f63ef Iustin Pop
    raw_data = fd.read()
1945 f65f63ef Iustin Pop
  finally:
1946 f65f63ef Iustin Pop
    fd.close()
1947 f65f63ef Iustin Pop
1948 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1949 f65f63ef Iustin Pop
  return rows[-lines:]
1950 f65f63ef Iustin Pop
1951 f65f63ef Iustin Pop
1952 26f15862 Iustin Pop
def SafeEncode(text):
1953 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1954 26f15862 Iustin Pop

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

1964 26f15862 Iustin Pop
  @type text: str or unicode
1965 26f15862 Iustin Pop
  @param text: input data
1966 26f15862 Iustin Pop
  @rtype: str
1967 26f15862 Iustin Pop
  @return: a safe version of text
1968 26f15862 Iustin Pop

1969 26f15862 Iustin Pop
  """
1970 d392fa34 Iustin Pop
  if isinstance(text, unicode):
1971 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
1972 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
1973 d392fa34 Iustin Pop
  resu = ""
1974 d392fa34 Iustin Pop
  for char in text:
1975 d392fa34 Iustin Pop
    c = ord(char)
1976 d392fa34 Iustin Pop
    if char  == '\t':
1977 d392fa34 Iustin Pop
      resu += r'\t'
1978 d392fa34 Iustin Pop
    elif char == '\n':
1979 d392fa34 Iustin Pop
      resu += r'\n'
1980 d392fa34 Iustin Pop
    elif char == '\r':
1981 d392fa34 Iustin Pop
      resu += r'\'r'
1982 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
1983 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
1984 d392fa34 Iustin Pop
    else:
1985 d392fa34 Iustin Pop
      resu += char
1986 d392fa34 Iustin Pop
  return resu
1987 26f15862 Iustin Pop
1988 26f15862 Iustin Pop
1989 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
1990 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
1991 5b69bc7c Iustin Pop

1992 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
1993 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
1994 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
1995 5b69bc7c Iustin Pop
  separator):
1996 5b69bc7c Iustin Pop
    - a plain , separates the elements
1997 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
1998 5b69bc7c Iustin Pop
      backslash plus a separator comma
1999 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2000 5b69bc7c Iustin Pop
      non-separator comma
2001 5b69bc7c Iustin Pop

2002 5b69bc7c Iustin Pop
  @type text: string
2003 5b69bc7c Iustin Pop
  @param text: the string to split
2004 5b69bc7c Iustin Pop
  @type sep: string
2005 5b69bc7c Iustin Pop
  @param text: the separator
2006 5b69bc7c Iustin Pop
  @rtype: string
2007 5b69bc7c Iustin Pop
  @return: a list of strings
2008 5b69bc7c Iustin Pop

2009 5b69bc7c Iustin Pop
  """
2010 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2011 5b69bc7c Iustin Pop
  slist = text.split(sep)
2012 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2013 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2014 5b69bc7c Iustin Pop
  rlist = []
2015 5b69bc7c Iustin Pop
  while slist:
2016 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2017 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2018 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2019 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2020 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2021 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2022 5b69bc7c Iustin Pop
        # the next step
2023 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2024 5b69bc7c Iustin Pop
        continue
2025 5b69bc7c Iustin Pop
    rlist.append(e1)
2026 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2027 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2028 5b69bc7c Iustin Pop
  return rlist
2029 5b69bc7c Iustin Pop
2030 5b69bc7c Iustin Pop
2031 ab3e6da8 Iustin Pop
def CommaJoin(names):
2032 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2033 ab3e6da8 Iustin Pop

2034 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2035 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2036 ab3e6da8 Iustin Pop

2037 ab3e6da8 Iustin Pop
  """
2038 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2039 ab3e6da8 Iustin Pop
2040 ab3e6da8 Iustin Pop
2041 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2042 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2043 3f6a47a8 Michael Hanselmann

2044 3f6a47a8 Michael Hanselmann
  @type value: int
2045 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2046 3f6a47a8 Michael Hanselmann
  @rtype: int
2047 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2048 3f6a47a8 Michael Hanselmann

2049 3f6a47a8 Michael Hanselmann
  """
2050 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2051 3f6a47a8 Michael Hanselmann
2052 3f6a47a8 Michael Hanselmann
2053 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2054 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2055 3f6a47a8 Michael Hanselmann

2056 3f6a47a8 Michael Hanselmann
  @type path: string
2057 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2058 3f6a47a8 Michael Hanselmann
  @rtype: int
2059 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2060 3f6a47a8 Michael Hanselmann

2061 3f6a47a8 Michael Hanselmann
  """
2062 3f6a47a8 Michael Hanselmann
  size = 0
2063 3f6a47a8 Michael Hanselmann
2064 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2065 2a887df9 Michael Hanselmann
    for filename in files:
2066 2a887df9 Michael Hanselmann
      st = os.lstat(os.path.join(curpath, filename))
2067 3f6a47a8 Michael Hanselmann
      size += st.st_size
2068 3f6a47a8 Michael Hanselmann
2069 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2070 3f6a47a8 Michael Hanselmann
2071 3f6a47a8 Michael Hanselmann
2072 620a85fd Iustin Pop
def GetFilesystemStats(path):
2073 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2074 3f6a47a8 Michael Hanselmann

2075 3f6a47a8 Michael Hanselmann
  @type path: string
2076 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2077 3f6a47a8 Michael Hanselmann
  @rtype: int
2078 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2079 3f6a47a8 Michael Hanselmann

2080 3f6a47a8 Michael Hanselmann
  """
2081 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2082 3f6a47a8 Michael Hanselmann
2083 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2084 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2085 620a85fd Iustin Pop
  return (tsize, fsize)
2086 3f6a47a8 Michael Hanselmann
2087 3f6a47a8 Michael Hanselmann
2088 eb58f7bd Michael Hanselmann
def RunInSeparateProcess(fn):
2089 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2090 eb58f7bd Michael Hanselmann

2091 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2092 eb58f7bd Michael Hanselmann

2093 eb58f7bd Michael Hanselmann
  @type fn: callable
2094 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2095 eb58f7bd Michael Hanselmann
  @rtype: tuple of (int/None, int/None)
2096 eb58f7bd Michael Hanselmann
  @return: Exit code and signal number
2097 eb58f7bd Michael Hanselmann

2098 eb58f7bd Michael Hanselmann
  """
2099 eb58f7bd Michael Hanselmann
  pid = os.fork()
2100 eb58f7bd Michael Hanselmann
  if pid == 0:
2101 eb58f7bd Michael Hanselmann
    # Child process
2102 eb58f7bd Michael Hanselmann
    try:
2103 82869978 Michael Hanselmann
      # In case the function uses temporary files
2104 82869978 Michael Hanselmann
      ResetTempfileModule()
2105 82869978 Michael Hanselmann
2106 eb58f7bd Michael Hanselmann
      # Call function
2107 eb58f7bd Michael Hanselmann
      result = int(bool(fn()))
2108 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2109 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2110 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2111 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2112 eb58f7bd Michael Hanselmann
      result = 33
2113 eb58f7bd Michael Hanselmann
2114 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2115 eb58f7bd Michael Hanselmann
2116 eb58f7bd Michael Hanselmann
  # Parent process
2117 eb58f7bd Michael Hanselmann
2118 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2119 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2120 eb58f7bd Michael Hanselmann
2121 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2122 eb58f7bd Michael Hanselmann
    exitcode = None
2123 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2124 eb58f7bd Michael Hanselmann
  else:
2125 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2126 eb58f7bd Michael Hanselmann
    signum = None
2127 eb58f7bd Michael Hanselmann
2128 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2129 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2130 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2131 eb58f7bd Michael Hanselmann
2132 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2133 eb58f7bd Michael Hanselmann
2134 eb58f7bd Michael Hanselmann
2135 7996a135 Iustin Pop
def LockedMethod(fn):
2136 7996a135 Iustin Pop
  """Synchronized object access decorator.
2137 7996a135 Iustin Pop

2138 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2139 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2140 7996a135 Iustin Pop

2141 7996a135 Iustin Pop
  """
2142 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2143 e67bd559 Michael Hanselmann
    if debug_locks:
2144 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2145 e67bd559 Michael Hanselmann
2146 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2147 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2148 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2149 7996a135 Iustin Pop
    lock = self._lock
2150 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2151 7996a135 Iustin Pop
    lock.acquire()
2152 7996a135 Iustin Pop
    try:
2153 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2154 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2155 7996a135 Iustin Pop
    finally:
2156 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2157 7996a135 Iustin Pop
      lock.release()
2158 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2159 7996a135 Iustin Pop
    return result
2160 7996a135 Iustin Pop
  return wrapper
2161 eb0f0ce0 Michael Hanselmann
2162 eb0f0ce0 Michael Hanselmann
2163 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2164 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2165 eb0f0ce0 Michael Hanselmann

2166 58885d79 Iustin Pop
  @type fd: int
2167 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2168 58885d79 Iustin Pop

2169 eb0f0ce0 Michael Hanselmann
  """
2170 eb0f0ce0 Michael Hanselmann
  try:
2171 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2172 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2173 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2174 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2175 eb0f0ce0 Michael Hanselmann
    raise
2176 de499029 Michael Hanselmann
2177 de499029 Michael Hanselmann
2178 3b813dd2 Iustin Pop
def FormatTime(val):
2179 3b813dd2 Iustin Pop
  """Formats a time value.
2180 3b813dd2 Iustin Pop

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

2185 3b813dd2 Iustin Pop
  """
2186 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2187 3b813dd2 Iustin Pop
    return "N/A"
2188 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2189 3b813dd2 Iustin Pop
  # platforms
2190 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2191 3b813dd2 Iustin Pop
2192 3b813dd2 Iustin Pop
2193 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2194 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2195 05e50653 Michael Hanselmann

2196 5cbe43a5 Michael Hanselmann
  @type filename: string
2197 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2198 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2199 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2200 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2201 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2202 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2203 5cbe43a5 Michael Hanselmann

2204 05e50653 Michael Hanselmann
  """
2205 05e50653 Michael Hanselmann
  if now is None:
2206 05e50653 Michael Hanselmann
    now = time.time()
2207 05e50653 Michael Hanselmann
2208 05e50653 Michael Hanselmann
  try:
2209 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2210 05e50653 Michael Hanselmann
  except IOError, err:
2211 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2212 05e50653 Michael Hanselmann
      raise
2213 05e50653 Michael Hanselmann
    value = None
2214 05e50653 Michael Hanselmann
2215 05e50653 Michael Hanselmann
  if value is not None:
2216 05e50653 Michael Hanselmann
    try:
2217 05e50653 Michael Hanselmann
      value = int(value)
2218 05e50653 Michael Hanselmann
    except ValueError:
2219 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2220 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2221 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2222 05e50653 Michael Hanselmann
      value = None
2223 05e50653 Michael Hanselmann
2224 05e50653 Michael Hanselmann
    if value is not None:
2225 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2226 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2227 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2228 5cbe43a5 Michael Hanselmann
        value = None
2229 5cbe43a5 Michael Hanselmann
2230 5cbe43a5 Michael Hanselmann
      elif now > value:
2231 05e50653 Michael Hanselmann
        value = None
2232 05e50653 Michael Hanselmann
2233 05e50653 Michael Hanselmann
  return value
2234 05e50653 Michael Hanselmann
2235 05e50653 Michael Hanselmann
2236 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2237 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2238 de0ea66b Michael Hanselmann

2239 de0ea66b Michael Hanselmann
  """
2240 de0ea66b Michael Hanselmann
2241 de0ea66b Michael Hanselmann
2242 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2243 de0ea66b Michael Hanselmann
  """Retry again.
2244 de0ea66b Michael Hanselmann

2245 de0ea66b Michael Hanselmann
  """
2246 de0ea66b Michael Hanselmann
2247 de0ea66b Michael Hanselmann
2248 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2249 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2250 de0ea66b Michael Hanselmann

2251 de0ea66b Michael Hanselmann
  """
2252 de0ea66b Michael Hanselmann
  __slots__ = [
2253 de0ea66b Michael Hanselmann
    "_factor",
2254 de0ea66b Michael Hanselmann
    "_limit",
2255 de0ea66b Michael Hanselmann
    "_next",
2256 de0ea66b Michael Hanselmann
    "_start",
2257 de0ea66b Michael Hanselmann
    ]
2258 de0ea66b Michael Hanselmann
2259 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2260 de0ea66b Michael Hanselmann
    """Initializes this class.
2261 de0ea66b Michael Hanselmann

2262 de0ea66b Michael Hanselmann
    @type start: float
2263 de0ea66b Michael Hanselmann
    @param start: Initial delay
2264 de0ea66b Michael Hanselmann
    @type factor: float
2265 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2266 de0ea66b Michael Hanselmann
    @type limit: float or None
2267 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2268 de0ea66b Michael Hanselmann

2269 de0ea66b Michael Hanselmann
    """
2270 de0ea66b Michael Hanselmann
    assert start > 0.0
2271 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2272 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2273 de0ea66b Michael Hanselmann
2274 de0ea66b Michael Hanselmann
    self._start = start
2275 de0ea66b Michael Hanselmann
    self._factor = factor
2276 de0ea66b Michael Hanselmann
    self._limit = limit
2277 de0ea66b Michael Hanselmann
2278 de0ea66b Michael Hanselmann
    self._next = start
2279 de0ea66b Michael Hanselmann
2280 de0ea66b Michael Hanselmann
  def __call__(self):
2281 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2282 de0ea66b Michael Hanselmann

2283 de0ea66b Michael Hanselmann
    """
2284 de0ea66b Michael Hanselmann
    current = self._next
2285 de0ea66b Michael Hanselmann
2286 de0ea66b Michael Hanselmann
    # Update for next run
2287 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2288 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2289 de0ea66b Michael Hanselmann
2290 de0ea66b Michael Hanselmann
    return current
2291 de0ea66b Michael Hanselmann
2292 de0ea66b Michael Hanselmann
2293 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2294 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2295 de0ea66b Michael Hanselmann
2296 de0ea66b Michael Hanselmann
2297 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2298 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2299 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2300 de0ea66b Michael Hanselmann

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

2305 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2306 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2307 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2308 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2309 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2310 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2311 de0ea66b Michael Hanselmann

2312 de0ea66b Michael Hanselmann
  @type fn: callable
2313 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2314 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2315 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2316 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2317 de0ea66b Michael Hanselmann
  @type timeout: float
2318 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2319 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2320 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2321 de0ea66b Michael Hanselmann
  @return: Return value of function
2322 de0ea66b Michael Hanselmann

2323 de0ea66b Michael Hanselmann
  """
2324 de0ea66b Michael Hanselmann
  assert callable(fn)
2325 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2326 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2327 de0ea66b Michael Hanselmann
2328 de0ea66b Michael Hanselmann
  if args is None:
2329 de0ea66b Michael Hanselmann
    args = []
2330 de0ea66b Michael Hanselmann
2331 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2332 de0ea66b Michael Hanselmann
2333 de0ea66b Michael Hanselmann
  if callable(delay):
2334 de0ea66b Michael Hanselmann
    # External function to calculate delay
2335 de0ea66b Michael Hanselmann
    calc_delay = delay
2336 de0ea66b Michael Hanselmann
2337 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2338 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2339 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2340 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2341 de0ea66b Michael Hanselmann
2342 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2343 de0ea66b Michael Hanselmann
    # Always use the remaining time
2344 de0ea66b Michael Hanselmann
    calc_delay = None
2345 de0ea66b Michael Hanselmann
2346 de0ea66b Michael Hanselmann
  else:
2347 de0ea66b Michael Hanselmann
    # Static delay
2348 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2349 de0ea66b Michael Hanselmann
2350 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2351 de0ea66b Michael Hanselmann
2352 de0ea66b Michael Hanselmann
  while True:
2353 de0ea66b Michael Hanselmann
    try:
2354 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2355 de0ea66b Michael Hanselmann
      return fn(*args)
2356 de0ea66b Michael Hanselmann
    except RetryAgain:
2357 de0ea66b Michael Hanselmann
      pass
2358 de0ea66b Michael Hanselmann
2359 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2360 de0ea66b Michael Hanselmann
2361 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2362 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2363 de0ea66b Michael Hanselmann
2364 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2365 de0ea66b Michael Hanselmann
2366 de0ea66b Michael Hanselmann
    if calc_delay is None:
2367 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2368 de0ea66b Michael Hanselmann
    else:
2369 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2370 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2371 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2372 de0ea66b Michael Hanselmann
2373 de0ea66b Michael Hanselmann
2374 a87b4824 Michael Hanselmann
class FileLock(object):
2375 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2376 a87b4824 Michael Hanselmann

2377 a87b4824 Michael Hanselmann
  """
2378 a87b4824 Michael Hanselmann
  def __init__(self, filename):
2379 58885d79 Iustin Pop
    """Constructor for FileLock.
2380 58885d79 Iustin Pop

2381 58885d79 Iustin Pop
    This will open the file denoted by the I{filename} argument.
2382 58885d79 Iustin Pop

2383 58885d79 Iustin Pop
    @type filename: str
2384 58885d79 Iustin Pop
    @param filename: path to the file to be locked
2385 58885d79 Iustin Pop

2386 58885d79 Iustin Pop
    """
2387 a87b4824 Michael Hanselmann
    self.filename = filename
2388 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
2389 a87b4824 Michael Hanselmann
2390 a87b4824 Michael Hanselmann
  def __del__(self):
2391 a87b4824 Michael Hanselmann
    self.Close()
2392 a87b4824 Michael Hanselmann
2393 a87b4824 Michael Hanselmann
  def Close(self):
2394 58885d79 Iustin Pop
    """Close the file and release the lock.
2395 58885d79 Iustin Pop

2396 58885d79 Iustin Pop
    """
2397 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2398 a87b4824 Michael Hanselmann
      self.fd.close()
2399 a87b4824 Michael Hanselmann
      self.fd = None
2400 a87b4824 Michael Hanselmann
2401 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2402 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2403 aa74b828 Michael Hanselmann

2404 aa74b828 Michael Hanselmann
    @type flag: int
2405 58885d79 Iustin Pop
    @param flag: operation flag
2406 aa74b828 Michael Hanselmann
    @type blocking: bool
2407 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2408 aa74b828 Michael Hanselmann
    @type timeout: None or float
2409 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2410 aa74b828 Michael Hanselmann
                    non-blocking mode).
2411 aa74b828 Michael Hanselmann
    @type errmsg: string
2412 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2413 aa74b828 Michael Hanselmann

2414 aa74b828 Michael Hanselmann
    """
2415 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2416 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2417 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2418 a87b4824 Michael Hanselmann
2419 aa74b828 Michael Hanselmann
    if timeout is not None:
2420 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2421 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
2422 a87b4824 Michael Hanselmann
2423 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
2424 aa74b828 Michael Hanselmann
    elif not blocking:
2425 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2426 aa74b828 Michael Hanselmann
      timeout_end = None
2427 aa74b828 Michael Hanselmann
2428 31892b4c Michael Hanselmann
    # TODO: Convert to utils.Retry
2429 31892b4c Michael Hanselmann
2430 aa74b828 Michael Hanselmann
    retry = True
2431 aa74b828 Michael Hanselmann
    while retry:
2432 aa74b828 Michael Hanselmann
      try:
2433 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
2434 aa74b828 Michael Hanselmann
        retry = False
2435 aa74b828 Michael Hanselmann
      except IOError, err:
2436 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
2437 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
2438 aa74b828 Michael Hanselmann
            # Wait before trying again
2439 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
2440 aa74b828 Michael Hanselmann
          else:
2441 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
2442 aa74b828 Michael Hanselmann
        else:
2443 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
2444 aa74b828 Michael Hanselmann
          raise
2445 aa74b828 Michael Hanselmann
2446 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2447 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2448 a87b4824 Michael Hanselmann

2449 58885d79 Iustin Pop
    @type blocking: boolean
2450 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2451 58885d79 Iustin Pop
        can lock the file or return immediately
2452 58885d79 Iustin Pop
    @type timeout: int or None
2453 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2454 58885d79 Iustin Pop
        (in blocking mode)
2455 58885d79 Iustin Pop

2456 a87b4824 Michael Hanselmann
    """
2457 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2458 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2459 a87b4824 Michael Hanselmann
2460 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2461 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2462 a87b4824 Michael Hanselmann

2463 58885d79 Iustin Pop
    @type blocking: boolean
2464 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2465 58885d79 Iustin Pop
        can lock the file or return immediately
2466 58885d79 Iustin Pop
    @type timeout: int or None
2467 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2468 58885d79 Iustin Pop
        (in blocking mode)
2469 58885d79 Iustin Pop

2470 a87b4824 Michael Hanselmann
    """
2471 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2472 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2473 a87b4824 Michael Hanselmann
2474 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2475 a87b4824 Michael Hanselmann
    """Unlocks the file.
2476 a87b4824 Michael Hanselmann

2477 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2478 58885d79 Iustin Pop
    operation::
2479 58885d79 Iustin Pop

2480 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2481 58885d79 Iustin Pop
      operations.
2482 58885d79 Iustin Pop

2483 58885d79 Iustin Pop
    @type blocking: boolean
2484 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2485 58885d79 Iustin Pop
        can lock the file or return immediately
2486 58885d79 Iustin Pop
    @type timeout: int or None
2487 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2488 58885d79 Iustin Pop
        (in blocking mode)
2489 a87b4824 Michael Hanselmann

2490 a87b4824 Michael Hanselmann
    """
2491 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2492 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2493 a87b4824 Michael Hanselmann
2494 a87b4824 Michael Hanselmann
2495 451575de Guido Trotter
def SignalHandled(signums):
2496 451575de Guido Trotter
  """Signal Handled decoration.
2497 451575de Guido Trotter

2498 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2499 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2500 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2501 451575de Guido Trotter
  objects as values.
2502 451575de Guido Trotter

2503 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2504 451575de Guido Trotter
  with different handlers.
2505 451575de Guido Trotter

2506 451575de Guido Trotter
  @type signums: list
2507 451575de Guido Trotter
  @param signums: signals to intercept
2508 451575de Guido Trotter

2509 451575de Guido Trotter
  """
2510 451575de Guido Trotter
  def wrap(fn):
2511 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2512 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2513 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2514 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2515 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2516 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2517 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2518 451575de Guido Trotter
      else:
2519 451575de Guido Trotter
        signal_handlers = {}
2520 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2521 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2522 451575de Guido Trotter
      try:
2523 451575de Guido Trotter
        for sig in signums:
2524 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2525 451575de Guido Trotter
        return fn(*args, **kwargs)
2526 451575de Guido Trotter
      finally:
2527 451575de Guido Trotter
        sighandler.Reset()
2528 451575de Guido Trotter
    return sig_function
2529 451575de Guido Trotter
  return wrap
2530 451575de Guido Trotter
2531 451575de Guido Trotter
2532 de499029 Michael Hanselmann
class SignalHandler(object):
2533 de499029 Michael Hanselmann
  """Generic signal handler class.
2534 de499029 Michael Hanselmann

2535 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2536 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2537 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2538 58885d79 Iustin Pop
  signal was sent.
2539 58885d79 Iustin Pop

2540 58885d79 Iustin Pop
  @type signum: list
2541 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2542 58885d79 Iustin Pop
  @type called: boolean
2543 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2544 de499029 Michael Hanselmann

2545 de499029 Michael Hanselmann
  """
2546 de499029 Michael Hanselmann
  def __init__(self, signum):
2547 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2548 de499029 Michael Hanselmann

2549 58885d79 Iustin Pop
    @type signum: int or list of ints
2550 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2551 de499029 Michael Hanselmann

2552 de499029 Michael Hanselmann
    """
2553 6c52849e Guido Trotter
    self.signum = set(signum)
2554 de499029 Michael Hanselmann
    self.called = False
2555 de499029 Michael Hanselmann
2556 de499029 Michael Hanselmann
    self._previous = {}
2557 de499029 Michael Hanselmann
    try:
2558 de499029 Michael Hanselmann
      for signum in self.signum:
2559 de499029 Michael Hanselmann
        # Setup handler
2560 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2561 de499029 Michael Hanselmann
        try:
2562 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2563 de499029 Michael Hanselmann
        except:
2564 de499029 Michael Hanselmann
          # Restore previous handler
2565 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2566 de499029 Michael Hanselmann
          raise
2567 de499029 Michael Hanselmann
    except:
2568 de499029 Michael Hanselmann
      # Reset all handlers
2569 de499029 Michael Hanselmann
      self.Reset()
2570 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2571 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2572 de499029 Michael Hanselmann
      raise
2573 de499029 Michael Hanselmann
2574 de499029 Michael Hanselmann
  def __del__(self):
2575 de499029 Michael Hanselmann
    self.Reset()
2576 de499029 Michael Hanselmann
2577 de499029 Michael Hanselmann
  def Reset(self):
2578 de499029 Michael Hanselmann
    """Restore previous handler.
2579 de499029 Michael Hanselmann

2580 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2581 58885d79 Iustin Pop

2582 de499029 Michael Hanselmann
    """
2583 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2584 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2585 de499029 Michael Hanselmann
      # If successful, remove from dict
2586 de499029 Michael Hanselmann
      del self._previous[signum]
2587 de499029 Michael Hanselmann
2588 de499029 Michael Hanselmann
  def Clear(self):
2589 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2590 de499029 Michael Hanselmann

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

2593 de499029 Michael Hanselmann
    """
2594 de499029 Michael Hanselmann
    self.called = False
2595 de499029 Michael Hanselmann
2596 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2597 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2598 de499029 Michael Hanselmann
    """Actual signal handling function.
2599 de499029 Michael Hanselmann

2600 de499029 Michael Hanselmann
    """
2601 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2602 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2603 de499029 Michael Hanselmann
    self.called = True
2604 a2d2e1a7 Iustin Pop
2605 a2d2e1a7 Iustin Pop
2606 a2d2e1a7 Iustin Pop
class FieldSet(object):
2607 a2d2e1a7 Iustin Pop
  """A simple field set.
2608 a2d2e1a7 Iustin Pop

2609 a2d2e1a7 Iustin Pop
  Among the features are:
2610 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2611 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2612 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2613 a2d2e1a7 Iustin Pop

2614 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2615 a2d2e1a7 Iustin Pop

2616 a2d2e1a7 Iustin Pop
  """
2617 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2618 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2619 a2d2e1a7 Iustin Pop
2620 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2621 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2622 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2623 a2d2e1a7 Iustin Pop
2624 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2625 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2626 a2d2e1a7 Iustin Pop

2627 a2d2e1a7 Iustin Pop
    @type field: str
2628 a2d2e1a7 Iustin Pop
    @param field: the string to match
2629 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2630 a2d2e1a7 Iustin Pop

2631 a2d2e1a7 Iustin Pop
    """
2632 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2633 a2d2e1a7 Iustin Pop
      return m
2634 6c881c52 Iustin Pop
    return None
2635 a2d2e1a7 Iustin Pop
2636 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2637 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2638 a2d2e1a7 Iustin Pop

2639 a2d2e1a7 Iustin Pop
    @type items: list
2640 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2641 a2d2e1a7 Iustin Pop
    @rtype: list
2642 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2643 a2d2e1a7 Iustin Pop

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