Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ e51db2a6

History | View | Annotate | Download (72 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 c4feafe8 Iustin Pop
    fname = PathJoin(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 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1611 b330ac0b Guido Trotter
1612 b330ac0b Guido Trotter
1613 2826b361 Guido Trotter
def EnsureDaemon(name):
1614 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1615 2826b361 Guido Trotter

1616 2826b361 Guido Trotter
  """
1617 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1618 2826b361 Guido Trotter
  if result.failed:
1619 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1620 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1621 2826b361 Guido Trotter
    return False
1622 2826b361 Guido Trotter
1623 2826b361 Guido Trotter
  return True
1624 2826b361 Guido Trotter
1625 2826b361 Guido Trotter
1626 b330ac0b Guido Trotter
def WritePidFile(name):
1627 b330ac0b Guido Trotter
  """Write the current process pidfile.
1628 b330ac0b Guido Trotter

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

1631 58885d79 Iustin Pop
  @type name: str
1632 58885d79 Iustin Pop
  @param name: the daemon name to use
1633 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1634 58885d79 Iustin Pop
      points to a live process
1635 b330ac0b Guido Trotter

1636 b330ac0b Guido Trotter
  """
1637 b330ac0b Guido Trotter
  pid = os.getpid()
1638 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1639 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1640 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1641 b330ac0b Guido Trotter
1642 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1643 b330ac0b Guido Trotter
1644 b330ac0b Guido Trotter
1645 b330ac0b Guido Trotter
def RemovePidFile(name):
1646 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1647 b330ac0b Guido Trotter

1648 b330ac0b Guido Trotter
  Any errors are ignored.
1649 b330ac0b Guido Trotter

1650 58885d79 Iustin Pop
  @type name: str
1651 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1652 58885d79 Iustin Pop

1653 b330ac0b Guido Trotter
  """
1654 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1655 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1656 b330ac0b Guido Trotter
  try:
1657 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1658 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1659 b330ac0b Guido Trotter
    pass
1660 b330ac0b Guido Trotter
1661 b330ac0b Guido Trotter
1662 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1663 ff5251bc Iustin Pop
                waitpid=False):
1664 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1665 b2a1f511 Iustin Pop

1666 b2a1f511 Iustin Pop
  @type pid: int
1667 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1668 38206f3c Iustin Pop
  @type signal_: int
1669 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1670 b2a1f511 Iustin Pop
  @type timeout: int
1671 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1672 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1673 b2a1f511 Iustin Pop
                  will be done
1674 ff5251bc Iustin Pop
  @type waitpid: boolean
1675 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1676 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1677 ff5251bc Iustin Pop
      would remain as zombie
1678 b2a1f511 Iustin Pop

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

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

1732 58885d79 Iustin Pop
  @type name: str
1733 58885d79 Iustin Pop
  @param name: the name to look for
1734 58885d79 Iustin Pop
  @type search_path: str
1735 58885d79 Iustin Pop
  @param search_path: location to start at
1736 58885d79 Iustin Pop
  @type test: callable
1737 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1738 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1739 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1740 58885d79 Iustin Pop
  @rtype: str or None
1741 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1742 57c177af Iustin Pop

1743 57c177af Iustin Pop
  """
1744 f95c81bf Iustin Pop
  # validate the filename mask
1745 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1746 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1747 f95c81bf Iustin Pop
                     name)
1748 f95c81bf Iustin Pop
    return None
1749 f95c81bf Iustin Pop
1750 57c177af Iustin Pop
  for dir_name in search_path:
1751 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1752 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1753 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1754 f95c81bf Iustin Pop
    # basename
1755 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1756 57c177af Iustin Pop
      return item_name
1757 57c177af Iustin Pop
  return None
1758 8d1a2a64 Michael Hanselmann
1759 8d1a2a64 Michael Hanselmann
1760 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1761 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1762 8d1a2a64 Michael Hanselmann

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

1766 58885d79 Iustin Pop
  @type vglist: dict
1767 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1768 58885d79 Iustin Pop
  @type vgname: str
1769 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1770 58885d79 Iustin Pop
  @type minsize: int
1771 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1772 58885d79 Iustin Pop
  @rtype: None or str
1773 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1774 8d1a2a64 Michael Hanselmann

1775 8d1a2a64 Michael Hanselmann
  """
1776 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1777 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1778 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1779 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1780 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1781 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1782 8d1a2a64 Michael Hanselmann
  return None
1783 7996a135 Iustin Pop
1784 7996a135 Iustin Pop
1785 45bc5e4a Michael Hanselmann
def SplitTime(value):
1786 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1787 739be818 Michael Hanselmann

1788 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1789 45bc5e4a Michael Hanselmann
  @type value: int or float
1790 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1791 739be818 Michael Hanselmann

1792 739be818 Michael Hanselmann
  """
1793 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1794 45bc5e4a Michael Hanselmann
1795 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1796 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1797 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1798 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1799 45bc5e4a Michael Hanselmann
1800 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1801 739be818 Michael Hanselmann
1802 739be818 Michael Hanselmann
1803 739be818 Michael Hanselmann
def MergeTime(timetuple):
1804 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1805 739be818 Michael Hanselmann

1806 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1807 739be818 Michael Hanselmann
  @type timetuple: tuple
1808 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1809 739be818 Michael Hanselmann

1810 739be818 Michael Hanselmann
  """
1811 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1812 739be818 Michael Hanselmann
1813 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1814 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1815 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1816 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1817 739be818 Michael Hanselmann
1818 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1819 739be818 Michael Hanselmann
1820 739be818 Michael Hanselmann
1821 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1822 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1823 4a8b186a Michael Hanselmann

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

1828 cd50653c Guido Trotter
  @type daemon_name: string
1829 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1830 58885d79 Iustin Pop
  @rtype: int
1831 58885d79 Iustin Pop

1832 4a8b186a Michael Hanselmann
  """
1833 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1834 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1835 cd50653c Guido Trotter
1836 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1837 4a8b186a Michael Hanselmann
  try:
1838 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1839 4a8b186a Michael Hanselmann
  except socket.error:
1840 cd50653c Guido Trotter
    port = default_port
1841 4a8b186a Michael Hanselmann
1842 4a8b186a Michael Hanselmann
  return port
1843 4a8b186a Michael Hanselmann
1844 4a8b186a Michael Hanselmann
1845 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
1846 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
1847 82d9caef Iustin Pop
  """Configures the logging module.
1848 82d9caef Iustin Pop

1849 58885d79 Iustin Pop
  @type logfile: str
1850 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1851 ea34193f Iustin Pop
  @type debug: integer
1852 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
1853 58885d79 Iustin Pop
      only those at C{INFO} and above level
1854 58885d79 Iustin Pop
  @type stderr_logging: boolean
1855 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1856 58885d79 Iustin Pop
  @type program: str
1857 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1858 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1859 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1860 551b6283 Iustin Pop
  @type syslog: string
1861 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
1862 551b6283 Iustin Pop
      - if no, syslog is not used
1863 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
1864 551b6283 Iustin Pop
      - if only, only syslog is used
1865 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1866 551b6283 Iustin Pop
      syslog/stderr logging is disabled
1867 58885d79 Iustin Pop

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

1934 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1935 da961187 Guido Trotter

1936 da961187 Guido Trotter
  """
1937 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1938 82d9caef Iustin Pop
1939 016d04b3 Michael Hanselmann
1940 4bb678e9 Iustin Pop
def PathJoin(*args):
1941 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
1942 4bb678e9 Iustin Pop

1943 4bb678e9 Iustin Pop
  Requirements:
1944 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
1945 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
1946 4bb678e9 Iustin Pop
        since we check for normalization at the end
1947 4bb678e9 Iustin Pop

1948 4bb678e9 Iustin Pop
  @param args: the path components to be joined
1949 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
1950 4bb678e9 Iustin Pop

1951 4bb678e9 Iustin Pop
  """
1952 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
1953 4bb678e9 Iustin Pop
  assert args
1954 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
1955 4bb678e9 Iustin Pop
  root = args[0]
1956 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
1957 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
1958 4bb678e9 Iustin Pop
  result = os.path.join(*args)
1959 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
1960 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
1961 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
1962 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
1963 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
1964 4bb678e9 Iustin Pop
  if prefix != root:
1965 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
1966 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
1967 4bb678e9 Iustin Pop
  return result
1968 4bb678e9 Iustin Pop
1969 4bb678e9 Iustin Pop
1970 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1971 f65f63ef Iustin Pop
  """Return the last lines from a file.
1972 f65f63ef Iustin Pop

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

1977 f65f63ef Iustin Pop
  @param fname: the file name
1978 f65f63ef Iustin Pop
  @type lines: int
1979 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1980 f65f63ef Iustin Pop

1981 f65f63ef Iustin Pop
  """
1982 f65f63ef Iustin Pop
  fd = open(fname, "r")
1983 f65f63ef Iustin Pop
  try:
1984 f65f63ef Iustin Pop
    fd.seek(0, 2)
1985 f65f63ef Iustin Pop
    pos = fd.tell()
1986 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1987 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1988 f65f63ef Iustin Pop
    raw_data = fd.read()
1989 f65f63ef Iustin Pop
  finally:
1990 f65f63ef Iustin Pop
    fd.close()
1991 f65f63ef Iustin Pop
1992 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1993 f65f63ef Iustin Pop
  return rows[-lines:]
1994 f65f63ef Iustin Pop
1995 f65f63ef Iustin Pop
1996 26f15862 Iustin Pop
def SafeEncode(text):
1997 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1998 26f15862 Iustin Pop

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

2008 26f15862 Iustin Pop
  @type text: str or unicode
2009 26f15862 Iustin Pop
  @param text: input data
2010 26f15862 Iustin Pop
  @rtype: str
2011 26f15862 Iustin Pop
  @return: a safe version of text
2012 26f15862 Iustin Pop

2013 26f15862 Iustin Pop
  """
2014 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2015 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2016 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2017 d392fa34 Iustin Pop
  resu = ""
2018 d392fa34 Iustin Pop
  for char in text:
2019 d392fa34 Iustin Pop
    c = ord(char)
2020 d392fa34 Iustin Pop
    if char  == '\t':
2021 d392fa34 Iustin Pop
      resu += r'\t'
2022 d392fa34 Iustin Pop
    elif char == '\n':
2023 d392fa34 Iustin Pop
      resu += r'\n'
2024 d392fa34 Iustin Pop
    elif char == '\r':
2025 d392fa34 Iustin Pop
      resu += r'\'r'
2026 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2027 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2028 d392fa34 Iustin Pop
    else:
2029 d392fa34 Iustin Pop
      resu += char
2030 d392fa34 Iustin Pop
  return resu
2031 26f15862 Iustin Pop
2032 26f15862 Iustin Pop
2033 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2034 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2035 5b69bc7c Iustin Pop

2036 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2037 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2038 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2039 5b69bc7c Iustin Pop
  separator):
2040 5b69bc7c Iustin Pop
    - a plain , separates the elements
2041 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2042 5b69bc7c Iustin Pop
      backslash plus a separator comma
2043 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2044 5b69bc7c Iustin Pop
      non-separator comma
2045 5b69bc7c Iustin Pop

2046 5b69bc7c Iustin Pop
  @type text: string
2047 5b69bc7c Iustin Pop
  @param text: the string to split
2048 5b69bc7c Iustin Pop
  @type sep: string
2049 5b69bc7c Iustin Pop
  @param text: the separator
2050 5b69bc7c Iustin Pop
  @rtype: string
2051 5b69bc7c Iustin Pop
  @return: a list of strings
2052 5b69bc7c Iustin Pop

2053 5b69bc7c Iustin Pop
  """
2054 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2055 5b69bc7c Iustin Pop
  slist = text.split(sep)
2056 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2057 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2058 5b69bc7c Iustin Pop
  rlist = []
2059 5b69bc7c Iustin Pop
  while slist:
2060 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2061 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2062 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2063 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2064 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2065 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2066 5b69bc7c Iustin Pop
        # the next step
2067 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2068 5b69bc7c Iustin Pop
        continue
2069 5b69bc7c Iustin Pop
    rlist.append(e1)
2070 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2071 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2072 5b69bc7c Iustin Pop
  return rlist
2073 5b69bc7c Iustin Pop
2074 5b69bc7c Iustin Pop
2075 ab3e6da8 Iustin Pop
def CommaJoin(names):
2076 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2077 ab3e6da8 Iustin Pop

2078 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2079 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2080 ab3e6da8 Iustin Pop

2081 ab3e6da8 Iustin Pop
  """
2082 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2083 ab3e6da8 Iustin Pop
2084 ab3e6da8 Iustin Pop
2085 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2086 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2087 3f6a47a8 Michael Hanselmann

2088 3f6a47a8 Michael Hanselmann
  @type value: int
2089 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2090 3f6a47a8 Michael Hanselmann
  @rtype: int
2091 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2092 3f6a47a8 Michael Hanselmann

2093 3f6a47a8 Michael Hanselmann
  """
2094 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2095 3f6a47a8 Michael Hanselmann
2096 3f6a47a8 Michael Hanselmann
2097 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2098 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2099 3f6a47a8 Michael Hanselmann

2100 3f6a47a8 Michael Hanselmann
  @type path: string
2101 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2102 3f6a47a8 Michael Hanselmann
  @rtype: int
2103 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2104 3f6a47a8 Michael Hanselmann

2105 3f6a47a8 Michael Hanselmann
  """
2106 3f6a47a8 Michael Hanselmann
  size = 0
2107 3f6a47a8 Michael Hanselmann
2108 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2109 2a887df9 Michael Hanselmann
    for filename in files:
2110 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2111 3f6a47a8 Michael Hanselmann
      size += st.st_size
2112 3f6a47a8 Michael Hanselmann
2113 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2114 3f6a47a8 Michael Hanselmann
2115 3f6a47a8 Michael Hanselmann
2116 620a85fd Iustin Pop
def GetFilesystemStats(path):
2117 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2118 3f6a47a8 Michael Hanselmann

2119 3f6a47a8 Michael Hanselmann
  @type path: string
2120 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2121 3f6a47a8 Michael Hanselmann
  @rtype: int
2122 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2123 3f6a47a8 Michael Hanselmann

2124 3f6a47a8 Michael Hanselmann
  """
2125 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2126 3f6a47a8 Michael Hanselmann
2127 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2128 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2129 620a85fd Iustin Pop
  return (tsize, fsize)
2130 3f6a47a8 Michael Hanselmann
2131 3f6a47a8 Michael Hanselmann
2132 eb58f7bd Michael Hanselmann
def RunInSeparateProcess(fn):
2133 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2134 eb58f7bd Michael Hanselmann

2135 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2136 eb58f7bd Michael Hanselmann

2137 eb58f7bd Michael Hanselmann
  @type fn: callable
2138 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2139 eb58f7bd Michael Hanselmann
  @rtype: tuple of (int/None, int/None)
2140 eb58f7bd Michael Hanselmann
  @return: Exit code and signal number
2141 eb58f7bd Michael Hanselmann

2142 eb58f7bd Michael Hanselmann
  """
2143 eb58f7bd Michael Hanselmann
  pid = os.fork()
2144 eb58f7bd Michael Hanselmann
  if pid == 0:
2145 eb58f7bd Michael Hanselmann
    # Child process
2146 eb58f7bd Michael Hanselmann
    try:
2147 82869978 Michael Hanselmann
      # In case the function uses temporary files
2148 82869978 Michael Hanselmann
      ResetTempfileModule()
2149 82869978 Michael Hanselmann
2150 eb58f7bd Michael Hanselmann
      # Call function
2151 eb58f7bd Michael Hanselmann
      result = int(bool(fn()))
2152 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2153 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2154 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2155 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2156 eb58f7bd Michael Hanselmann
      result = 33
2157 eb58f7bd Michael Hanselmann
2158 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2159 eb58f7bd Michael Hanselmann
2160 eb58f7bd Michael Hanselmann
  # Parent process
2161 eb58f7bd Michael Hanselmann
2162 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2163 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2164 eb58f7bd Michael Hanselmann
2165 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2166 eb58f7bd Michael Hanselmann
    exitcode = None
2167 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2168 eb58f7bd Michael Hanselmann
  else:
2169 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2170 eb58f7bd Michael Hanselmann
    signum = None
2171 eb58f7bd Michael Hanselmann
2172 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2173 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2174 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2175 eb58f7bd Michael Hanselmann
2176 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2177 eb58f7bd Michael Hanselmann
2178 eb58f7bd Michael Hanselmann
2179 7996a135 Iustin Pop
def LockedMethod(fn):
2180 7996a135 Iustin Pop
  """Synchronized object access decorator.
2181 7996a135 Iustin Pop

2182 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2183 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2184 7996a135 Iustin Pop

2185 7996a135 Iustin Pop
  """
2186 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2187 e67bd559 Michael Hanselmann
    if debug_locks:
2188 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2189 e67bd559 Michael Hanselmann
2190 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2191 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2192 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2193 7996a135 Iustin Pop
    lock = self._lock
2194 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2195 7996a135 Iustin Pop
    lock.acquire()
2196 7996a135 Iustin Pop
    try:
2197 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2198 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2199 7996a135 Iustin Pop
    finally:
2200 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2201 7996a135 Iustin Pop
      lock.release()
2202 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2203 7996a135 Iustin Pop
    return result
2204 7996a135 Iustin Pop
  return wrapper
2205 eb0f0ce0 Michael Hanselmann
2206 eb0f0ce0 Michael Hanselmann
2207 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2208 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2209 eb0f0ce0 Michael Hanselmann

2210 58885d79 Iustin Pop
  @type fd: int
2211 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2212 58885d79 Iustin Pop

2213 eb0f0ce0 Michael Hanselmann
  """
2214 eb0f0ce0 Michael Hanselmann
  try:
2215 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2216 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2217 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2218 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2219 eb0f0ce0 Michael Hanselmann
    raise
2220 de499029 Michael Hanselmann
2221 de499029 Michael Hanselmann
2222 3b813dd2 Iustin Pop
def FormatTime(val):
2223 3b813dd2 Iustin Pop
  """Formats a time value.
2224 3b813dd2 Iustin Pop

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

2229 3b813dd2 Iustin Pop
  """
2230 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2231 3b813dd2 Iustin Pop
    return "N/A"
2232 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2233 3b813dd2 Iustin Pop
  # platforms
2234 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2235 3b813dd2 Iustin Pop
2236 3b813dd2 Iustin Pop
2237 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2238 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2239 05e50653 Michael Hanselmann

2240 5cbe43a5 Michael Hanselmann
  @type filename: string
2241 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2242 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2243 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2244 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2245 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2246 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2247 5cbe43a5 Michael Hanselmann

2248 05e50653 Michael Hanselmann
  """
2249 05e50653 Michael Hanselmann
  if now is None:
2250 05e50653 Michael Hanselmann
    now = time.time()
2251 05e50653 Michael Hanselmann
2252 05e50653 Michael Hanselmann
  try:
2253 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2254 05e50653 Michael Hanselmann
  except IOError, err:
2255 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2256 05e50653 Michael Hanselmann
      raise
2257 05e50653 Michael Hanselmann
    value = None
2258 05e50653 Michael Hanselmann
2259 05e50653 Michael Hanselmann
  if value is not None:
2260 05e50653 Michael Hanselmann
    try:
2261 05e50653 Michael Hanselmann
      value = int(value)
2262 05e50653 Michael Hanselmann
    except ValueError:
2263 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2264 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2265 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2266 05e50653 Michael Hanselmann
      value = None
2267 05e50653 Michael Hanselmann
2268 05e50653 Michael Hanselmann
    if value is not None:
2269 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2270 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2271 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2272 5cbe43a5 Michael Hanselmann
        value = None
2273 5cbe43a5 Michael Hanselmann
2274 5cbe43a5 Michael Hanselmann
      elif now > value:
2275 05e50653 Michael Hanselmann
        value = None
2276 05e50653 Michael Hanselmann
2277 05e50653 Michael Hanselmann
  return value
2278 05e50653 Michael Hanselmann
2279 05e50653 Michael Hanselmann
2280 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2281 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2282 de0ea66b Michael Hanselmann

2283 de0ea66b Michael Hanselmann
  """
2284 de0ea66b Michael Hanselmann
2285 de0ea66b Michael Hanselmann
2286 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2287 de0ea66b Michael Hanselmann
  """Retry again.
2288 de0ea66b Michael Hanselmann

2289 de0ea66b Michael Hanselmann
  """
2290 de0ea66b Michael Hanselmann
2291 de0ea66b Michael Hanselmann
2292 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2293 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2294 de0ea66b Michael Hanselmann

2295 de0ea66b Michael Hanselmann
  """
2296 de0ea66b Michael Hanselmann
  __slots__ = [
2297 de0ea66b Michael Hanselmann
    "_factor",
2298 de0ea66b Michael Hanselmann
    "_limit",
2299 de0ea66b Michael Hanselmann
    "_next",
2300 de0ea66b Michael Hanselmann
    "_start",
2301 de0ea66b Michael Hanselmann
    ]
2302 de0ea66b Michael Hanselmann
2303 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2304 de0ea66b Michael Hanselmann
    """Initializes this class.
2305 de0ea66b Michael Hanselmann

2306 de0ea66b Michael Hanselmann
    @type start: float
2307 de0ea66b Michael Hanselmann
    @param start: Initial delay
2308 de0ea66b Michael Hanselmann
    @type factor: float
2309 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2310 de0ea66b Michael Hanselmann
    @type limit: float or None
2311 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2312 de0ea66b Michael Hanselmann

2313 de0ea66b Michael Hanselmann
    """
2314 de0ea66b Michael Hanselmann
    assert start > 0.0
2315 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2316 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2317 de0ea66b Michael Hanselmann
2318 de0ea66b Michael Hanselmann
    self._start = start
2319 de0ea66b Michael Hanselmann
    self._factor = factor
2320 de0ea66b Michael Hanselmann
    self._limit = limit
2321 de0ea66b Michael Hanselmann
2322 de0ea66b Michael Hanselmann
    self._next = start
2323 de0ea66b Michael Hanselmann
2324 de0ea66b Michael Hanselmann
  def __call__(self):
2325 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2326 de0ea66b Michael Hanselmann

2327 de0ea66b Michael Hanselmann
    """
2328 de0ea66b Michael Hanselmann
    current = self._next
2329 de0ea66b Michael Hanselmann
2330 de0ea66b Michael Hanselmann
    # Update for next run
2331 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2332 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2333 de0ea66b Michael Hanselmann
2334 de0ea66b Michael Hanselmann
    return current
2335 de0ea66b Michael Hanselmann
2336 de0ea66b Michael Hanselmann
2337 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2338 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2339 de0ea66b Michael Hanselmann
2340 de0ea66b Michael Hanselmann
2341 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2342 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2343 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2344 de0ea66b Michael Hanselmann

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

2349 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2350 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2351 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2352 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2353 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2354 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2355 de0ea66b Michael Hanselmann

2356 de0ea66b Michael Hanselmann
  @type fn: callable
2357 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2358 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2359 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2360 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2361 de0ea66b Michael Hanselmann
  @type timeout: float
2362 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2363 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2364 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2365 de0ea66b Michael Hanselmann
  @return: Return value of function
2366 de0ea66b Michael Hanselmann

2367 de0ea66b Michael Hanselmann
  """
2368 de0ea66b Michael Hanselmann
  assert callable(fn)
2369 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2370 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2371 de0ea66b Michael Hanselmann
2372 de0ea66b Michael Hanselmann
  if args is None:
2373 de0ea66b Michael Hanselmann
    args = []
2374 de0ea66b Michael Hanselmann
2375 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2376 de0ea66b Michael Hanselmann
2377 de0ea66b Michael Hanselmann
  if callable(delay):
2378 de0ea66b Michael Hanselmann
    # External function to calculate delay
2379 de0ea66b Michael Hanselmann
    calc_delay = delay
2380 de0ea66b Michael Hanselmann
2381 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2382 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2383 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2384 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2385 de0ea66b Michael Hanselmann
2386 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2387 de0ea66b Michael Hanselmann
    # Always use the remaining time
2388 de0ea66b Michael Hanselmann
    calc_delay = None
2389 de0ea66b Michael Hanselmann
2390 de0ea66b Michael Hanselmann
  else:
2391 de0ea66b Michael Hanselmann
    # Static delay
2392 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2393 de0ea66b Michael Hanselmann
2394 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2395 de0ea66b Michael Hanselmann
2396 de0ea66b Michael Hanselmann
  while True:
2397 de0ea66b Michael Hanselmann
    try:
2398 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2399 de0ea66b Michael Hanselmann
      return fn(*args)
2400 de0ea66b Michael Hanselmann
    except RetryAgain:
2401 de0ea66b Michael Hanselmann
      pass
2402 de0ea66b Michael Hanselmann
2403 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2404 de0ea66b Michael Hanselmann
2405 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2406 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2407 de0ea66b Michael Hanselmann
2408 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2409 de0ea66b Michael Hanselmann
2410 de0ea66b Michael Hanselmann
    if calc_delay is None:
2411 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2412 de0ea66b Michael Hanselmann
    else:
2413 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2414 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2415 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2416 de0ea66b Michael Hanselmann
2417 de0ea66b Michael Hanselmann
2418 a87b4824 Michael Hanselmann
class FileLock(object):
2419 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2420 a87b4824 Michael Hanselmann

2421 a87b4824 Michael Hanselmann
  """
2422 a87b4824 Michael Hanselmann
  def __init__(self, filename):
2423 58885d79 Iustin Pop
    """Constructor for FileLock.
2424 58885d79 Iustin Pop

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

2427 58885d79 Iustin Pop
    @type filename: str
2428 58885d79 Iustin Pop
    @param filename: path to the file to be locked
2429 58885d79 Iustin Pop

2430 58885d79 Iustin Pop
    """
2431 a87b4824 Michael Hanselmann
    self.filename = filename
2432 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
2433 a87b4824 Michael Hanselmann
2434 a87b4824 Michael Hanselmann
  def __del__(self):
2435 a87b4824 Michael Hanselmann
    self.Close()
2436 a87b4824 Michael Hanselmann
2437 a87b4824 Michael Hanselmann
  def Close(self):
2438 58885d79 Iustin Pop
    """Close the file and release the lock.
2439 58885d79 Iustin Pop

2440 58885d79 Iustin Pop
    """
2441 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2442 a87b4824 Michael Hanselmann
      self.fd.close()
2443 a87b4824 Michael Hanselmann
      self.fd = None
2444 a87b4824 Michael Hanselmann
2445 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2446 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2447 aa74b828 Michael Hanselmann

2448 aa74b828 Michael Hanselmann
    @type flag: int
2449 58885d79 Iustin Pop
    @param flag: operation flag
2450 aa74b828 Michael Hanselmann
    @type blocking: bool
2451 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2452 aa74b828 Michael Hanselmann
    @type timeout: None or float
2453 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2454 aa74b828 Michael Hanselmann
                    non-blocking mode).
2455 aa74b828 Michael Hanselmann
    @type errmsg: string
2456 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2457 aa74b828 Michael Hanselmann

2458 aa74b828 Michael Hanselmann
    """
2459 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2460 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2461 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2462 a87b4824 Michael Hanselmann
2463 aa74b828 Michael Hanselmann
    if timeout is not None:
2464 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2465 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
2466 a87b4824 Michael Hanselmann
2467 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
2468 aa74b828 Michael Hanselmann
    elif not blocking:
2469 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2470 aa74b828 Michael Hanselmann
      timeout_end = None
2471 aa74b828 Michael Hanselmann
2472 31892b4c Michael Hanselmann
    # TODO: Convert to utils.Retry
2473 31892b4c Michael Hanselmann
2474 aa74b828 Michael Hanselmann
    retry = True
2475 aa74b828 Michael Hanselmann
    while retry:
2476 aa74b828 Michael Hanselmann
      try:
2477 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
2478 aa74b828 Michael Hanselmann
        retry = False
2479 aa74b828 Michael Hanselmann
      except IOError, err:
2480 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
2481 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
2482 aa74b828 Michael Hanselmann
            # Wait before trying again
2483 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
2484 aa74b828 Michael Hanselmann
          else:
2485 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
2486 aa74b828 Michael Hanselmann
        else:
2487 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
2488 aa74b828 Michael Hanselmann
          raise
2489 aa74b828 Michael Hanselmann
2490 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2491 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2492 a87b4824 Michael Hanselmann

2493 58885d79 Iustin Pop
    @type blocking: boolean
2494 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2495 58885d79 Iustin Pop
        can lock the file or return immediately
2496 58885d79 Iustin Pop
    @type timeout: int or None
2497 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2498 58885d79 Iustin Pop
        (in blocking mode)
2499 58885d79 Iustin Pop

2500 a87b4824 Michael Hanselmann
    """
2501 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2502 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2503 a87b4824 Michael Hanselmann
2504 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2505 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2506 a87b4824 Michael Hanselmann

2507 58885d79 Iustin Pop
    @type blocking: boolean
2508 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2509 58885d79 Iustin Pop
        can lock the file or return immediately
2510 58885d79 Iustin Pop
    @type timeout: int or None
2511 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2512 58885d79 Iustin Pop
        (in blocking mode)
2513 58885d79 Iustin Pop

2514 a87b4824 Michael Hanselmann
    """
2515 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2516 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2517 a87b4824 Michael Hanselmann
2518 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2519 a87b4824 Michael Hanselmann
    """Unlocks the file.
2520 a87b4824 Michael Hanselmann

2521 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2522 58885d79 Iustin Pop
    operation::
2523 58885d79 Iustin Pop

2524 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2525 58885d79 Iustin Pop
      operations.
2526 58885d79 Iustin Pop

2527 58885d79 Iustin Pop
    @type blocking: boolean
2528 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2529 58885d79 Iustin Pop
        can lock the file or return immediately
2530 58885d79 Iustin Pop
    @type timeout: int or None
2531 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2532 58885d79 Iustin Pop
        (in blocking mode)
2533 a87b4824 Michael Hanselmann

2534 a87b4824 Michael Hanselmann
    """
2535 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2536 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2537 a87b4824 Michael Hanselmann
2538 a87b4824 Michael Hanselmann
2539 451575de Guido Trotter
def SignalHandled(signums):
2540 451575de Guido Trotter
  """Signal Handled decoration.
2541 451575de Guido Trotter

2542 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2543 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2544 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2545 451575de Guido Trotter
  objects as values.
2546 451575de Guido Trotter

2547 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2548 451575de Guido Trotter
  with different handlers.
2549 451575de Guido Trotter

2550 451575de Guido Trotter
  @type signums: list
2551 451575de Guido Trotter
  @param signums: signals to intercept
2552 451575de Guido Trotter

2553 451575de Guido Trotter
  """
2554 451575de Guido Trotter
  def wrap(fn):
2555 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2556 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2557 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2558 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2559 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2560 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2561 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2562 451575de Guido Trotter
      else:
2563 451575de Guido Trotter
        signal_handlers = {}
2564 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2565 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2566 451575de Guido Trotter
      try:
2567 451575de Guido Trotter
        for sig in signums:
2568 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2569 451575de Guido Trotter
        return fn(*args, **kwargs)
2570 451575de Guido Trotter
      finally:
2571 451575de Guido Trotter
        sighandler.Reset()
2572 451575de Guido Trotter
    return sig_function
2573 451575de Guido Trotter
  return wrap
2574 451575de Guido Trotter
2575 451575de Guido Trotter
2576 de499029 Michael Hanselmann
class SignalHandler(object):
2577 de499029 Michael Hanselmann
  """Generic signal handler class.
2578 de499029 Michael Hanselmann

2579 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2580 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2581 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2582 58885d79 Iustin Pop
  signal was sent.
2583 58885d79 Iustin Pop

2584 58885d79 Iustin Pop
  @type signum: list
2585 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2586 58885d79 Iustin Pop
  @type called: boolean
2587 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2588 de499029 Michael Hanselmann

2589 de499029 Michael Hanselmann
  """
2590 de499029 Michael Hanselmann
  def __init__(self, signum):
2591 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2592 de499029 Michael Hanselmann

2593 58885d79 Iustin Pop
    @type signum: int or list of ints
2594 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2595 de499029 Michael Hanselmann

2596 de499029 Michael Hanselmann
    """
2597 6c52849e Guido Trotter
    self.signum = set(signum)
2598 de499029 Michael Hanselmann
    self.called = False
2599 de499029 Michael Hanselmann
2600 de499029 Michael Hanselmann
    self._previous = {}
2601 de499029 Michael Hanselmann
    try:
2602 de499029 Michael Hanselmann
      for signum in self.signum:
2603 de499029 Michael Hanselmann
        # Setup handler
2604 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2605 de499029 Michael Hanselmann
        try:
2606 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2607 de499029 Michael Hanselmann
        except:
2608 de499029 Michael Hanselmann
          # Restore previous handler
2609 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2610 de499029 Michael Hanselmann
          raise
2611 de499029 Michael Hanselmann
    except:
2612 de499029 Michael Hanselmann
      # Reset all handlers
2613 de499029 Michael Hanselmann
      self.Reset()
2614 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2615 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2616 de499029 Michael Hanselmann
      raise
2617 de499029 Michael Hanselmann
2618 de499029 Michael Hanselmann
  def __del__(self):
2619 de499029 Michael Hanselmann
    self.Reset()
2620 de499029 Michael Hanselmann
2621 de499029 Michael Hanselmann
  def Reset(self):
2622 de499029 Michael Hanselmann
    """Restore previous handler.
2623 de499029 Michael Hanselmann

2624 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2625 58885d79 Iustin Pop

2626 de499029 Michael Hanselmann
    """
2627 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2628 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2629 de499029 Michael Hanselmann
      # If successful, remove from dict
2630 de499029 Michael Hanselmann
      del self._previous[signum]
2631 de499029 Michael Hanselmann
2632 de499029 Michael Hanselmann
  def Clear(self):
2633 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2634 de499029 Michael Hanselmann

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

2637 de499029 Michael Hanselmann
    """
2638 de499029 Michael Hanselmann
    self.called = False
2639 de499029 Michael Hanselmann
2640 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2641 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2642 de499029 Michael Hanselmann
    """Actual signal handling function.
2643 de499029 Michael Hanselmann

2644 de499029 Michael Hanselmann
    """
2645 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2646 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2647 de499029 Michael Hanselmann
    self.called = True
2648 a2d2e1a7 Iustin Pop
2649 a2d2e1a7 Iustin Pop
2650 a2d2e1a7 Iustin Pop
class FieldSet(object):
2651 a2d2e1a7 Iustin Pop
  """A simple field set.
2652 a2d2e1a7 Iustin Pop

2653 a2d2e1a7 Iustin Pop
  Among the features are:
2654 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2655 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2656 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2657 a2d2e1a7 Iustin Pop

2658 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2659 a2d2e1a7 Iustin Pop

2660 a2d2e1a7 Iustin Pop
  """
2661 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2662 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2663 a2d2e1a7 Iustin Pop
2664 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2665 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2666 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2667 a2d2e1a7 Iustin Pop
2668 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2669 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2670 a2d2e1a7 Iustin Pop

2671 a2d2e1a7 Iustin Pop
    @type field: str
2672 a2d2e1a7 Iustin Pop
    @param field: the string to match
2673 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2674 a2d2e1a7 Iustin Pop

2675 a2d2e1a7 Iustin Pop
    """
2676 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2677 a2d2e1a7 Iustin Pop
      return m
2678 6c881c52 Iustin Pop
    return None
2679 a2d2e1a7 Iustin Pop
2680 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2681 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2682 a2d2e1a7 Iustin Pop

2683 a2d2e1a7 Iustin Pop
    @type items: list
2684 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2685 a2d2e1a7 Iustin Pop
    @rtype: list
2686 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2687 a2d2e1a7 Iustin Pop

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