Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ b4478d34

History | View | Annotate | Download (73.4 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 58885d79 Iustin Pop
"""Ganeti utility module.
23 58885d79 Iustin Pop

24 58885d79 Iustin Pop
This module holds functions that can be used in both daemons (all) and
25 58885d79 Iustin Pop
the command line scripts.
26 899d2a81 Michael Hanselmann

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import os
31 a8083063 Iustin Pop
import time
32 113b55aa Iustin Pop
import subprocess
33 a8083063 Iustin Pop
import re
34 a8083063 Iustin Pop
import socket
35 a8083063 Iustin Pop
import tempfile
36 a8083063 Iustin Pop
import shutil
37 4ca1b175 Alexander Schreiber
import errno
38 2f8b60b3 Iustin Pop
import pwd
39 78feb6fb Guido Trotter
import itertools
40 9c233417 Iustin Pop
import select
41 9c233417 Iustin Pop
import fcntl
42 8f765069 Iustin Pop
import resource
43 bb698c1f Iustin Pop
import logging
44 551b6283 Iustin Pop
import logging.handlers
45 de499029 Michael Hanselmann
import signal
46 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 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
619 26288e68 Iustin Pop
620 89e1fc26 Iustin Pop
  def __init__(self, name=None):
621 bcf043c9 Iustin Pop
    """Initialize the host name object.
622 bcf043c9 Iustin Pop

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

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

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

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

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

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

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

661 89e1fc26 Iustin Pop
    """
662 89e1fc26 Iustin Pop
    try:
663 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
664 89e1fc26 Iustin Pop
    except socket.gaierror, err:
665 89e1fc26 Iustin Pop
      # hostname not found in DNS
666 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
667 a8083063 Iustin Pop
668 89e1fc26 Iustin Pop
    return result
669 a8083063 Iustin Pop
670 26288e68 Iustin Pop
  @classmethod
671 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
672 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
673 26288e68 Iustin Pop

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

678 26288e68 Iustin Pop
    """
679 26288e68 Iustin Pop
    hostname = hostname.lower()
680 26288e68 Iustin Pop
    if (not cls._VALID_NAME_RE.match(hostname) or
681 26288e68 Iustin Pop
        # double-dots, meaning empty label
682 26288e68 Iustin Pop
        ".." in hostname or
683 26288e68 Iustin Pop
        # empty initial label
684 26288e68 Iustin Pop
        hostname.startswith(".")):
685 26288e68 Iustin Pop
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
686 26288e68 Iustin Pop
                                 errors.ECODE_INVAL)
687 26288e68 Iustin Pop
    if hostname.endswith("."):
688 26288e68 Iustin Pop
      hostname = hostname.rstrip(".")
689 26288e68 Iustin Pop
    return hostname
690 26288e68 Iustin Pop
691 a8083063 Iustin Pop
692 104f4ca1 Iustin Pop
def GetHostInfo(name=None):
693 104f4ca1 Iustin Pop
  """Lookup host name and raise an OpPrereqError for failures"""
694 104f4ca1 Iustin Pop
695 104f4ca1 Iustin Pop
  try:
696 104f4ca1 Iustin Pop
    return HostInfo(name)
697 104f4ca1 Iustin Pop
  except errors.ResolverError, err:
698 104f4ca1 Iustin Pop
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
699 104f4ca1 Iustin Pop
                               (err[0], err[2]), errors.ECODE_RESOLVER)
700 104f4ca1 Iustin Pop
701 104f4ca1 Iustin Pop
702 a8083063 Iustin Pop
def ListVolumeGroups():
703 a8083063 Iustin Pop
  """List volume groups and their size
704 a8083063 Iustin Pop

705 58885d79 Iustin Pop
  @rtype: dict
706 58885d79 Iustin Pop
  @return:
707 58885d79 Iustin Pop
       Dictionary with keys volume name and values
708 58885d79 Iustin Pop
       the size of the volume
709 a8083063 Iustin Pop

710 a8083063 Iustin Pop
  """
711 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
712 a8083063 Iustin Pop
  result = RunCmd(command)
713 a8083063 Iustin Pop
  retval = {}
714 a8083063 Iustin Pop
  if result.failed:
715 a8083063 Iustin Pop
    return retval
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
718 a8083063 Iustin Pop
    try:
719 a8083063 Iustin Pop
      name, size = line.split()
720 a8083063 Iustin Pop
      size = int(float(size))
721 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
722 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
723 a8083063 Iustin Pop
      continue
724 a8083063 Iustin Pop
725 a8083063 Iustin Pop
    retval[name] = size
726 a8083063 Iustin Pop
727 a8083063 Iustin Pop
  return retval
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
730 a8083063 Iustin Pop
def BridgeExists(bridge):
731 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
732 a8083063 Iustin Pop

733 58885d79 Iustin Pop
  @type bridge: str
734 58885d79 Iustin Pop
  @param bridge: the bridge name to check
735 58885d79 Iustin Pop
  @rtype: boolean
736 58885d79 Iustin Pop
  @return: True if it does
737 a8083063 Iustin Pop

738 a8083063 Iustin Pop
  """
739 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
740 a8083063 Iustin Pop
741 a8083063 Iustin Pop
742 a8083063 Iustin Pop
def NiceSort(name_list):
743 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
744 a8083063 Iustin Pop

745 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
746 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
747 58885d79 Iustin Pop
  'a11']}.
748 a8083063 Iustin Pop

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

753 58885d79 Iustin Pop
  @type name_list: list
754 58885d79 Iustin Pop
  @param name_list: the names to be sorted
755 58885d79 Iustin Pop
  @rtype: list
756 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
757 a8083063 Iustin Pop

758 a8083063 Iustin Pop
  """
759 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
760 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
761 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
762 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
763 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
764 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
765 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
766 a8083063 Iustin Pop
  def _TryInt(val):
767 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
768 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
769 a8083063 Iustin Pop
      return val
770 a8083063 Iustin Pop
    rval = int(val)
771 a8083063 Iustin Pop
    return rval
772 a8083063 Iustin Pop
773 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
774 a8083063 Iustin Pop
             for name in name_list]
775 a8083063 Iustin Pop
  to_sort.sort()
776 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
777 a8083063 Iustin Pop
778 a8083063 Iustin Pop
779 a8083063 Iustin Pop
def TryConvert(fn, val):
780 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
781 a8083063 Iustin Pop

782 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
783 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
784 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
785 58885d79 Iustin Pop
  exceptions are propagated to the caller.
786 58885d79 Iustin Pop

787 58885d79 Iustin Pop
  @type fn: callable
788 58885d79 Iustin Pop
  @param fn: function to apply to the value
789 58885d79 Iustin Pop
  @param val: the value to be converted
790 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
791 58885d79 Iustin Pop
      otherwise the original value.
792 a8083063 Iustin Pop

793 a8083063 Iustin Pop
  """
794 a8083063 Iustin Pop
  try:
795 a8083063 Iustin Pop
    nv = fn(val)
796 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
797 a8083063 Iustin Pop
    nv = val
798 a8083063 Iustin Pop
  return nv
799 a8083063 Iustin Pop
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
def IsValidIP(ip):
802 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
803 a8083063 Iustin Pop

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

807 58885d79 Iustin Pop
  @type ip: str
808 58885d79 Iustin Pop
  @param ip: the address to be checked
809 58885d79 Iustin Pop
  @rtype: a regular expression match object
810 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
811 58885d79 Iustin Pop
      address is not valid
812 a8083063 Iustin Pop

813 a8083063 Iustin Pop
  """
814 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
815 58885d79 Iustin Pop
  #TODO: convert and return only boolean
816 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
817 a8083063 Iustin Pop
818 a8083063 Iustin Pop
819 a8083063 Iustin Pop
def IsValidShellParam(word):
820 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
821 a8083063 Iustin Pop

822 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
823 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
824 a8083063 Iustin Pop
  the actual command.
825 a8083063 Iustin Pop

826 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
827 a8083063 Iustin Pop
  side.
828 a8083063 Iustin Pop

829 58885d79 Iustin Pop
  @type word: str
830 58885d79 Iustin Pop
  @param word: the word to check
831 58885d79 Iustin Pop
  @rtype: boolean
832 58885d79 Iustin Pop
  @return: True if the word is 'safe'
833 58885d79 Iustin Pop

834 a8083063 Iustin Pop
  """
835 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
836 a8083063 Iustin Pop
837 a8083063 Iustin Pop
838 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
839 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
840 a8083063 Iustin Pop

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

846 58885d79 Iustin Pop
  @type template: str
847 58885d79 Iustin Pop
  @param template: the string holding the template for the
848 58885d79 Iustin Pop
      string formatting
849 58885d79 Iustin Pop
  @rtype: str
850 58885d79 Iustin Pop
  @return: the expanded command line
851 58885d79 Iustin Pop

852 a8083063 Iustin Pop
  """
853 a8083063 Iustin Pop
  for word in args:
854 a8083063 Iustin Pop
    if not IsValidShellParam(word):
855 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
856 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
857 a8083063 Iustin Pop
  return template % args
858 a8083063 Iustin Pop
859 a8083063 Iustin Pop
860 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
861 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
862 a8083063 Iustin Pop

863 58885d79 Iustin Pop
  @type value: int
864 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
865 9fbfbb7b Iustin Pop
  @type units: char
866 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
867 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
868 9fbfbb7b Iustin Pop
      - 'm' for MiBs
869 9fbfbb7b Iustin Pop
      - 'g' for GiBs
870 9fbfbb7b Iustin Pop
      - 't' for TiBs
871 58885d79 Iustin Pop
  @rtype: str
872 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
873 a8083063 Iustin Pop

874 a8083063 Iustin Pop
  """
875 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
876 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
877 a8083063 Iustin Pop
878 9fbfbb7b Iustin Pop
  suffix = ''
879 9fbfbb7b Iustin Pop
880 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
881 9fbfbb7b Iustin Pop
    if units == 'h':
882 9fbfbb7b Iustin Pop
      suffix = 'M'
883 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
884 9fbfbb7b Iustin Pop
885 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
886 9fbfbb7b Iustin Pop
    if units == 'h':
887 9fbfbb7b Iustin Pop
      suffix = 'G'
888 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
889 a8083063 Iustin Pop
890 a8083063 Iustin Pop
  else:
891 9fbfbb7b Iustin Pop
    if units == 'h':
892 9fbfbb7b Iustin Pop
      suffix = 'T'
893 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
894 a8083063 Iustin Pop
895 a8083063 Iustin Pop
896 a8083063 Iustin Pop
def ParseUnit(input_string):
897 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
898 a8083063 Iustin Pop

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

903 a8083063 Iustin Pop
  """
904 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
905 a8083063 Iustin Pop
  if not m:
906 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
907 a8083063 Iustin Pop
908 a8083063 Iustin Pop
  value = float(m.groups()[0])
909 a8083063 Iustin Pop
910 a8083063 Iustin Pop
  unit = m.groups()[1]
911 a8083063 Iustin Pop
  if unit:
912 a8083063 Iustin Pop
    lcunit = unit.lower()
913 a8083063 Iustin Pop
  else:
914 a8083063 Iustin Pop
    lcunit = 'm'
915 a8083063 Iustin Pop
916 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
917 a8083063 Iustin Pop
    # Value already in MiB
918 a8083063 Iustin Pop
    pass
919 a8083063 Iustin Pop
920 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
921 a8083063 Iustin Pop
    value *= 1024
922 a8083063 Iustin Pop
923 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
924 a8083063 Iustin Pop
    value *= 1024 * 1024
925 a8083063 Iustin Pop
926 a8083063 Iustin Pop
  else:
927 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
928 a8083063 Iustin Pop
929 a8083063 Iustin Pop
  # Make sure we round up
930 a8083063 Iustin Pop
  if int(value) < value:
931 a8083063 Iustin Pop
    value += 1
932 a8083063 Iustin Pop
933 a8083063 Iustin Pop
  # Round up to the next multiple of 4
934 a8083063 Iustin Pop
  value = int(value)
935 a8083063 Iustin Pop
  if value % 4:
936 a8083063 Iustin Pop
    value += 4 - value % 4
937 a8083063 Iustin Pop
938 a8083063 Iustin Pop
  return value
939 a8083063 Iustin Pop
940 a8083063 Iustin Pop
941 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
942 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
943 a8083063 Iustin Pop

944 58885d79 Iustin Pop
  @type file_name: str
945 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
946 58885d79 Iustin Pop
  @type key: str
947 58885d79 Iustin Pop
  @param key: string containing key
948 58885d79 Iustin Pop

949 a8083063 Iustin Pop
  """
950 a8083063 Iustin Pop
  key_fields = key.split()
951 a8083063 Iustin Pop
952 a8083063 Iustin Pop
  f = open(file_name, 'a+')
953 a8083063 Iustin Pop
  try:
954 a8083063 Iustin Pop
    nl = True
955 a8083063 Iustin Pop
    for line in f:
956 a8083063 Iustin Pop
      # Ignore whitespace changes
957 a8083063 Iustin Pop
      if line.split() == key_fields:
958 a8083063 Iustin Pop
        break
959 a8083063 Iustin Pop
      nl = line.endswith('\n')
960 a8083063 Iustin Pop
    else:
961 a8083063 Iustin Pop
      if not nl:
962 a8083063 Iustin Pop
        f.write("\n")
963 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
964 a8083063 Iustin Pop
      f.write("\n")
965 a8083063 Iustin Pop
      f.flush()
966 a8083063 Iustin Pop
  finally:
967 a8083063 Iustin Pop
    f.close()
968 a8083063 Iustin Pop
969 a8083063 Iustin Pop
970 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
971 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
972 a8083063 Iustin Pop

973 58885d79 Iustin Pop
  @type file_name: str
974 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
975 58885d79 Iustin Pop
  @type key: str
976 58885d79 Iustin Pop
  @param key: string containing key
977 58885d79 Iustin Pop

978 a8083063 Iustin Pop
  """
979 a8083063 Iustin Pop
  key_fields = key.split()
980 a8083063 Iustin Pop
981 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
982 a8083063 Iustin Pop
  try:
983 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
984 a8083063 Iustin Pop
    try:
985 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
986 59f82e3f Michael Hanselmann
      try:
987 59f82e3f Michael Hanselmann
        for line in f:
988 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
989 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
990 59f82e3f Michael Hanselmann
            out.write(line)
991 899d2a81 Michael Hanselmann
992 899d2a81 Michael Hanselmann
        out.flush()
993 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
994 899d2a81 Michael Hanselmann
      finally:
995 899d2a81 Michael Hanselmann
        f.close()
996 899d2a81 Michael Hanselmann
    finally:
997 899d2a81 Michael Hanselmann
      out.close()
998 899d2a81 Michael Hanselmann
  except:
999 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1000 899d2a81 Michael Hanselmann
    raise
1001 899d2a81 Michael Hanselmann
1002 899d2a81 Michael Hanselmann
1003 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1004 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1005 899d2a81 Michael Hanselmann

1006 58885d79 Iustin Pop
  @type file_name: str
1007 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1008 58885d79 Iustin Pop
  @type ip: str
1009 58885d79 Iustin Pop
  @param ip: the IP address
1010 58885d79 Iustin Pop
  @type hostname: str
1011 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1012 58885d79 Iustin Pop
  @type aliases: list
1013 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1014 58885d79 Iustin Pop

1015 899d2a81 Michael Hanselmann
  """
1016 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1017 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1018 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1019 7fbb1f65 Michael Hanselmann
1020 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1021 899d2a81 Michael Hanselmann
  try:
1022 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1023 9440aeab Michael Hanselmann
    try:
1024 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1025 9440aeab Michael Hanselmann
      try:
1026 9440aeab Michael Hanselmann
        for line in f:
1027 9440aeab Michael Hanselmann
          fields = line.split()
1028 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1029 9440aeab Michael Hanselmann
            continue
1030 9440aeab Michael Hanselmann
          out.write(line)
1031 9440aeab Michael Hanselmann
1032 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1033 9440aeab Michael Hanselmann
        if aliases:
1034 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1035 9440aeab Michael Hanselmann
        out.write('\n')
1036 9440aeab Michael Hanselmann
1037 9440aeab Michael Hanselmann
        out.flush()
1038 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1039 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1040 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1041 9440aeab Michael Hanselmann
      finally:
1042 9440aeab Michael Hanselmann
        f.close()
1043 9440aeab Michael Hanselmann
    finally:
1044 9440aeab Michael Hanselmann
      out.close()
1045 9440aeab Michael Hanselmann
  except:
1046 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1047 9440aeab Michael Hanselmann
    raise
1048 899d2a81 Michael Hanselmann
1049 899d2a81 Michael Hanselmann
1050 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1051 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1052 d9c02ca6 Michael Hanselmann

1053 58885d79 Iustin Pop
  @type hostname: str
1054 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1055 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1056 58885d79 Iustin Pop

1057 d9c02ca6 Michael Hanselmann
  """
1058 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1059 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1060 d9c02ca6 Michael Hanselmann
1061 d9c02ca6 Michael Hanselmann
1062 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1063 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1064 899d2a81 Michael Hanselmann

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

1067 58885d79 Iustin Pop
  @type file_name: str
1068 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1069 58885d79 Iustin Pop
  @type hostname: str
1070 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1071 58885d79 Iustin Pop

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

1109 58885d79 Iustin Pop
  @type hostname: str
1110 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1111 58885d79 Iustin Pop
      full and shot name will be removed from
1112 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1113 58885d79 Iustin Pop

1114 d9c02ca6 Michael Hanselmann
  """
1115 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1116 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1117 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1118 d9c02ca6 Michael Hanselmann
1119 d9c02ca6 Michael Hanselmann
1120 a8083063 Iustin Pop
def CreateBackup(file_name):
1121 a8083063 Iustin Pop
  """Creates a backup of a file.
1122 a8083063 Iustin Pop

1123 58885d79 Iustin Pop
  @type file_name: str
1124 58885d79 Iustin Pop
  @param file_name: file to be backed up
1125 58885d79 Iustin Pop
  @rtype: str
1126 58885d79 Iustin Pop
  @return: the path to the newly created backup
1127 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1128 a8083063 Iustin Pop

1129 a8083063 Iustin Pop
  """
1130 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1131 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1132 3ecf6786 Iustin Pop
                                file_name)
1133 a8083063 Iustin Pop
1134 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
1135 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1136 081b1e69 Michael Hanselmann
1137 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1138 081b1e69 Michael Hanselmann
  try:
1139 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1140 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1141 081b1e69 Michael Hanselmann
    try:
1142 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1143 081b1e69 Michael Hanselmann
    finally:
1144 081b1e69 Michael Hanselmann
      fdst.close()
1145 081b1e69 Michael Hanselmann
  finally:
1146 081b1e69 Michael Hanselmann
    fsrc.close()
1147 081b1e69 Michael Hanselmann
1148 a8083063 Iustin Pop
  return backup_name
1149 a8083063 Iustin Pop
1150 a8083063 Iustin Pop
1151 a8083063 Iustin Pop
def ShellQuote(value):
1152 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1153 3ecf6786 Iustin Pop

1154 58885d79 Iustin Pop
  @type value: str
1155 58885d79 Iustin Pop
  @param value: the argument to be quoted
1156 58885d79 Iustin Pop
  @rtype: str
1157 58885d79 Iustin Pop
  @return: the quoted value
1158 58885d79 Iustin Pop

1159 a8083063 Iustin Pop
  """
1160 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1161 a8083063 Iustin Pop
    return value
1162 a8083063 Iustin Pop
  else:
1163 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1164 a8083063 Iustin Pop
1165 a8083063 Iustin Pop
1166 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1167 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1168 58885d79 Iustin Pop

1169 58885d79 Iustin Pop
  @type args: list
1170 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1171 58885d79 Iustin Pop
  @rtype: str
1172 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1173 a8083063 Iustin Pop

1174 a8083063 Iustin Pop
  """
1175 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1176 88d14415 Michael Hanselmann
1177 88d14415 Michael Hanselmann
1178 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1179 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1180 2c30e9d7 Alexander Schreiber

1181 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1182 58885d79 Iustin Pop
  to it.
1183 58885d79 Iustin Pop

1184 58885d79 Iustin Pop
  @type target: str
1185 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1186 58885d79 Iustin Pop
  @type port: int
1187 58885d79 Iustin Pop
  @param port: the port to connect to
1188 58885d79 Iustin Pop
  @type timeout: int
1189 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1190 58885d79 Iustin Pop
  @type live_port_needed: boolean
1191 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1192 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1193 58885d79 Iustin Pop
  @type source: str or None
1194 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1195 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1196 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1197 2c30e9d7 Alexander Schreiber

1198 2c30e9d7 Alexander Schreiber
  """
1199 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1200 2c30e9d7 Alexander Schreiber
1201 0b5ad33e Iustin Pop
  success = False
1202 2c30e9d7 Alexander Schreiber
1203 b15d625f Iustin Pop
  if source is not None:
1204 b15d625f Iustin Pop
    try:
1205 b15d625f Iustin Pop
      sock.bind((source, 0))
1206 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1207 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1208 b15d625f Iustin Pop
        success = False
1209 2c30e9d7 Alexander Schreiber
1210 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1211 2c30e9d7 Alexander Schreiber
1212 2c30e9d7 Alexander Schreiber
  try:
1213 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1214 2c30e9d7 Alexander Schreiber
    sock.close()
1215 2c30e9d7 Alexander Schreiber
    success = True
1216 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1217 2c30e9d7 Alexander Schreiber
    success = False
1218 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1219 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1220 2c30e9d7 Alexander Schreiber
1221 2c30e9d7 Alexander Schreiber
  return success
1222 eedbda4b Michael Hanselmann
1223 eedbda4b Michael Hanselmann
1224 caad16e2 Iustin Pop
def OwnIpAddress(address):
1225 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1226 caad16e2 Iustin Pop

1227 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1228 caad16e2 Iustin Pop
  address.
1229 caad16e2 Iustin Pop

1230 caad16e2 Iustin Pop
  @type address: string
1231 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1232 caad16e2 Iustin Pop
  @rtype: bool
1233 58885d79 Iustin Pop
  @return: True if we own the address
1234 caad16e2 Iustin Pop

1235 caad16e2 Iustin Pop
  """
1236 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1237 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1238 caad16e2 Iustin Pop
1239 caad16e2 Iustin Pop
1240 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1241 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1242 58885d79 Iustin Pop

1243 58885d79 Iustin Pop
  @type path: str
1244 58885d79 Iustin Pop
  @param path: the directory to enumerate
1245 58885d79 Iustin Pop
  @rtype: list
1246 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1247 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1248 eedbda4b Michael Hanselmann

1249 eedbda4b Michael Hanselmann
  """
1250 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1251 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1252 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1253 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1254 f3299a07 Michael Hanselmann
  files.sort()
1255 f3299a07 Michael Hanselmann
  return files
1256 2f8b60b3 Iustin Pop
1257 2f8b60b3 Iustin Pop
1258 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1259 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1260 257f4c0a Iustin Pop

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

1265 2f8b60b3 Iustin Pop
  """
1266 2f8b60b3 Iustin Pop
  try:
1267 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1268 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1269 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1270 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1271 257f4c0a Iustin Pop
    else:
1272 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1273 257f4c0a Iustin Pop
                                   type(user))
1274 2f8b60b3 Iustin Pop
  except KeyError:
1275 2f8b60b3 Iustin Pop
    return default
1276 2f8b60b3 Iustin Pop
  return result.pw_dir
1277 59072e7e Michael Hanselmann
1278 59072e7e Michael Hanselmann
1279 24818e8f Michael Hanselmann
def NewUUID():
1280 59072e7e Michael Hanselmann
  """Returns a random UUID.
1281 59072e7e Michael Hanselmann

1282 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1283 58885d79 Iustin Pop
      filesystem.
1284 58885d79 Iustin Pop
  @rtype: str
1285 58885d79 Iustin Pop

1286 59072e7e Michael Hanselmann
  """
1287 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1288 087b34fe Iustin Pop
1289 087b34fe Iustin Pop
1290 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1291 33081d90 Iustin Pop
  """Generates a random secret.
1292 33081d90 Iustin Pop

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

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

1301 33081d90 Iustin Pop
  """
1302 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1303 33081d90 Iustin Pop
1304 33081d90 Iustin Pop
1305 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1306 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1307 9dae41ad Guido Trotter

1308 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1309 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1310 9dae41ad Guido Trotter

1311 9dae41ad Guido Trotter
  """
1312 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1313 9dae41ad Guido Trotter
    try:
1314 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1315 9dae41ad Guido Trotter
    except EnvironmentError, err:
1316 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1317 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1318 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1319 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1320 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1321 9dae41ad Guido Trotter
1322 9dae41ad Guido Trotter
1323 016308cb Iustin Pop
def ReadFile(file_name, size=-1):
1324 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1325 ca0aa6d0 Michael Hanselmann

1326 016308cb Iustin Pop
  @type size: int
1327 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1328 58885d79 Iustin Pop
  @rtype: str
1329 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1330 ca0aa6d0 Michael Hanselmann

1331 ca0aa6d0 Michael Hanselmann
  """
1332 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1333 ca0aa6d0 Michael Hanselmann
  try:
1334 016308cb Iustin Pop
    return f.read(size)
1335 ca0aa6d0 Michael Hanselmann
  finally:
1336 ca0aa6d0 Michael Hanselmann
    f.close()
1337 ca0aa6d0 Michael Hanselmann
1338 ca0aa6d0 Michael Hanselmann
1339 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1340 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1341 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1342 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1343 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1344 087b34fe Iustin Pop
  """(Over)write a file atomically.
1345 087b34fe Iustin Pop

1346 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1347 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1348 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1349 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1350 087b34fe Iustin Pop
  mtime/atime of the file.
1351 087b34fe Iustin Pop

1352 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1353 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1354 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1355 087b34fe Iustin Pop
  temporary file should be removed.
1356 087b34fe Iustin Pop

1357 58885d79 Iustin Pop
  @type file_name: str
1358 58885d79 Iustin Pop
  @param file_name: the target filename
1359 58885d79 Iustin Pop
  @type fn: callable
1360 58885d79 Iustin Pop
  @param fn: content writing function, called with
1361 58885d79 Iustin Pop
      file descriptor as parameter
1362 69efe319 Michael Hanselmann
  @type data: str
1363 58885d79 Iustin Pop
  @param data: contents of the file
1364 58885d79 Iustin Pop
  @type mode: int
1365 58885d79 Iustin Pop
  @param mode: file mode
1366 58885d79 Iustin Pop
  @type uid: int
1367 58885d79 Iustin Pop
  @param uid: the owner of the file
1368 58885d79 Iustin Pop
  @type gid: int
1369 58885d79 Iustin Pop
  @param gid: the group of the file
1370 58885d79 Iustin Pop
  @type atime: int
1371 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1372 58885d79 Iustin Pop
  @type mtime: int
1373 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1374 58885d79 Iustin Pop
  @type close: boolean
1375 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1376 58885d79 Iustin Pop
  @type prewrite: callable
1377 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1378 58885d79 Iustin Pop
  @type postwrite: callable
1379 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1380 58885d79 Iustin Pop

1381 58885d79 Iustin Pop
  @rtype: None or int
1382 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1383 58885d79 Iustin Pop
      otherwise the file descriptor
1384 58885d79 Iustin Pop

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

1387 087b34fe Iustin Pop
  """
1388 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1389 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1390 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1391 087b34fe Iustin Pop
1392 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1393 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1394 087b34fe Iustin Pop
1395 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1396 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1397 087b34fe Iustin Pop
                                 " set or None")
1398 087b34fe Iustin Pop
1399 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1400 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1401 087b34fe Iustin Pop
1402 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1403 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1404 81b7354c Iustin Pop
  do_remove = True
1405 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1406 087b34fe Iustin Pop
  # leaves it in place
1407 087b34fe Iustin Pop
  try:
1408 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1409 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1410 087b34fe Iustin Pop
    if mode:
1411 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1412 71714516 Michael Hanselmann
    if callable(prewrite):
1413 71714516 Michael Hanselmann
      prewrite(fd)
1414 087b34fe Iustin Pop
    if data is not None:
1415 087b34fe Iustin Pop
      os.write(fd, data)
1416 087b34fe Iustin Pop
    else:
1417 087b34fe Iustin Pop
      fn(fd)
1418 71714516 Michael Hanselmann
    if callable(postwrite):
1419 71714516 Michael Hanselmann
      postwrite(fd)
1420 087b34fe Iustin Pop
    os.fsync(fd)
1421 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1422 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1423 70f4497c Michael Hanselmann
    if not dry_run:
1424 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1425 81b7354c Iustin Pop
      do_remove = False
1426 087b34fe Iustin Pop
  finally:
1427 71714516 Michael Hanselmann
    if close:
1428 71714516 Michael Hanselmann
      os.close(fd)
1429 71714516 Michael Hanselmann
      result = None
1430 71714516 Michael Hanselmann
    else:
1431 71714516 Michael Hanselmann
      result = fd
1432 81b7354c Iustin Pop
    if do_remove:
1433 81b7354c Iustin Pop
      RemoveFile(new_name)
1434 78feb6fb Guido Trotter
1435 71714516 Michael Hanselmann
  return result
1436 71714516 Michael Hanselmann
1437 78feb6fb Guido Trotter
1438 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1439 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1440 7b4126b7 Iustin Pop

1441 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1442 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1443 7b4126b7 Iustin Pop
  value, the index will be returned.
1444 7b4126b7 Iustin Pop

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

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

1450 58885d79 Iustin Pop
  @type seq: sequence
1451 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1452 58885d79 Iustin Pop
  @type base: int
1453 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1454 58885d79 Iustin Pop
  @rtype: int
1455 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1456 7b4126b7 Iustin Pop

1457 7b4126b7 Iustin Pop
  """
1458 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1459 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1460 7b4126b7 Iustin Pop
    if elem > idx + base:
1461 7b4126b7 Iustin Pop
      # idx is not used
1462 7b4126b7 Iustin Pop
      return idx + base
1463 7b4126b7 Iustin Pop
  return None
1464 7b4126b7 Iustin Pop
1465 7b4126b7 Iustin Pop
1466 7260cfbe Iustin Pop
def all(seq, pred=bool): # pylint: disable-msg=W0622
1467 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1468 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilterfalse(pred, seq):
1469 78feb6fb Guido Trotter
    return False
1470 78feb6fb Guido Trotter
  return True
1471 78feb6fb Guido Trotter
1472 78feb6fb Guido Trotter
1473 7260cfbe Iustin Pop
def any(seq, pred=bool): # pylint: disable-msg=W0622
1474 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1475 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilter(pred, seq):
1476 78feb6fb Guido Trotter
    return True
1477 78feb6fb Guido Trotter
  return False
1478 f7414041 Michael Hanselmann
1479 f7414041 Michael Hanselmann
1480 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1481 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1482 f7414041 Michael Hanselmann

1483 f7414041 Michael Hanselmann
  Element order is preserved.
1484 58885d79 Iustin Pop

1485 58885d79 Iustin Pop
  @type seq: sequence
1486 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1487 58885d79 Iustin Pop
  @rtype: list
1488 58885d79 Iustin Pop
  @return: list of unique elements from seq
1489 58885d79 Iustin Pop

1490 f7414041 Michael Hanselmann
  """
1491 f7414041 Michael Hanselmann
  seen = set()
1492 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1493 1862d460 Alexander Schreiber
1494 1862d460 Alexander Schreiber
1495 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1496 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1497 1862d460 Alexander Schreiber

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

1501 58885d79 Iustin Pop
  @type mac: str
1502 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1503 82187135 Renรฉ Nussbaumer
  @rtype: str
1504 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1505 82187135 Renรฉ Nussbaumer

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

1508 1862d460 Alexander Schreiber
  """
1509 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1510 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1511 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1512 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1513 82187135 Renรฉ Nussbaumer
1514 82187135 Renรฉ Nussbaumer
  return mac.lower()
1515 06009e27 Iustin Pop
1516 06009e27 Iustin Pop
1517 06009e27 Iustin Pop
def TestDelay(duration):
1518 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1519 06009e27 Iustin Pop

1520 58885d79 Iustin Pop
  @type duration: float
1521 58885d79 Iustin Pop
  @param duration: the sleep duration
1522 58885d79 Iustin Pop
  @rtype: boolean
1523 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1524 58885d79 Iustin Pop

1525 06009e27 Iustin Pop
  """
1526 06009e27 Iustin Pop
  if duration < 0:
1527 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1528 06009e27 Iustin Pop
  time.sleep(duration)
1529 38ea42a1 Iustin Pop
  return True, None
1530 8f765069 Iustin Pop
1531 8f765069 Iustin Pop
1532 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1533 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1534 8f765069 Iustin Pop

1535 7d88772a Iustin Pop
  @type fd: int
1536 7d88772a Iustin Pop
  @param fd: the file descriptor
1537 7d88772a Iustin Pop
  @type retries: int
1538 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1539 7d88772a Iustin Pop
      other error than EBADF
1540 7d88772a Iustin Pop

1541 7d88772a Iustin Pop
  """
1542 7d88772a Iustin Pop
  try:
1543 7d88772a Iustin Pop
    os.close(fd)
1544 7d88772a Iustin Pop
  except OSError, err:
1545 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1546 7d88772a Iustin Pop
      if retries > 0:
1547 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1548 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1549 7d88772a Iustin Pop
    # ignore this and go on
1550 7d88772a Iustin Pop
1551 7d88772a Iustin Pop
1552 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1553 7d88772a Iustin Pop
  """Close file descriptors.
1554 7d88772a Iustin Pop

1555 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1556 7d88772a Iustin Pop
  stdin/out/err).
1557 8f765069 Iustin Pop

1558 58885d79 Iustin Pop
  @type noclose_fds: list or None
1559 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1560 58885d79 Iustin Pop
      that should not be closed
1561 58885d79 Iustin Pop

1562 8f765069 Iustin Pop
  """
1563 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1564 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1565 8f765069 Iustin Pop
    try:
1566 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1567 8f765069 Iustin Pop
      if MAXFD < 0:
1568 8f765069 Iustin Pop
        MAXFD = 1024
1569 8f765069 Iustin Pop
    except OSError:
1570 8f765069 Iustin Pop
      MAXFD = 1024
1571 8f765069 Iustin Pop
  else:
1572 8f765069 Iustin Pop
    MAXFD = 1024
1573 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1574 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1575 7d88772a Iustin Pop
    maxfd = MAXFD
1576 7d88772a Iustin Pop
1577 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1578 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1579 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1580 7d88772a Iustin Pop
      continue
1581 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1582 7d88772a Iustin Pop
1583 7d88772a Iustin Pop
1584 7d88772a Iustin Pop
def Daemonize(logfile):
1585 7d88772a Iustin Pop
  """Daemonize the current process.
1586 7d88772a Iustin Pop

1587 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1588 7d88772a Iustin Pop
  runs it in the background as a daemon.
1589 7d88772a Iustin Pop

1590 7d88772a Iustin Pop
  @type logfile: str
1591 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1592 7d88772a Iustin Pop
  @rtype: int
1593 5fcc718f Iustin Pop
  @return: the value zero
1594 7d88772a Iustin Pop

1595 7d88772a Iustin Pop
  """
1596 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1597 7260cfbe Iustin Pop
  # yes, we really want os._exit
1598 7d88772a Iustin Pop
  UMASK = 077
1599 7d88772a Iustin Pop
  WORKDIR = "/"
1600 8f765069 Iustin Pop
1601 8f765069 Iustin Pop
  # this might fail
1602 8f765069 Iustin Pop
  pid = os.fork()
1603 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1604 8f765069 Iustin Pop
    os.setsid()
1605 8f765069 Iustin Pop
    # this might fail
1606 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1607 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1608 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1609 8f765069 Iustin Pop
      os.umask(UMASK)
1610 8f765069 Iustin Pop
    else:
1611 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1612 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1613 8f765069 Iustin Pop
  else:
1614 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1615 8f765069 Iustin Pop
1616 7d88772a Iustin Pop
  for fd in range(3):
1617 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1618 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1619 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1620 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1621 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1622 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1623 7d88772a Iustin Pop
  os.dup2(1, 2)
1624 8f765069 Iustin Pop
  return 0
1625 57c177af Iustin Pop
1626 57c177af Iustin Pop
1627 53beffbb Iustin Pop
def DaemonPidFileName(name):
1628 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1629 58885d79 Iustin Pop

1630 58885d79 Iustin Pop
  @type name: str
1631 58885d79 Iustin Pop
  @param name: the daemon name
1632 58885d79 Iustin Pop
  @rtype: str
1633 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1634 58885d79 Iustin Pop
      daemon name
1635 b330ac0b Guido Trotter

1636 b330ac0b Guido Trotter
  """
1637 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1638 b330ac0b Guido Trotter
1639 b330ac0b Guido Trotter
1640 2826b361 Guido Trotter
def EnsureDaemon(name):
1641 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1642 2826b361 Guido Trotter

1643 2826b361 Guido Trotter
  """
1644 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1645 2826b361 Guido Trotter
  if result.failed:
1646 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1647 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1648 2826b361 Guido Trotter
    return False
1649 2826b361 Guido Trotter
1650 2826b361 Guido Trotter
  return True
1651 2826b361 Guido Trotter
1652 2826b361 Guido Trotter
1653 b330ac0b Guido Trotter
def WritePidFile(name):
1654 b330ac0b Guido Trotter
  """Write the current process pidfile.
1655 b330ac0b Guido Trotter

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

1658 58885d79 Iustin Pop
  @type name: str
1659 58885d79 Iustin Pop
  @param name: the daemon name to use
1660 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1661 58885d79 Iustin Pop
      points to a live process
1662 b330ac0b Guido Trotter

1663 b330ac0b Guido Trotter
  """
1664 b330ac0b Guido Trotter
  pid = os.getpid()
1665 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1666 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1667 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1668 b330ac0b Guido Trotter
1669 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1670 b330ac0b Guido Trotter
1671 b330ac0b Guido Trotter
1672 b330ac0b Guido Trotter
def RemovePidFile(name):
1673 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1674 b330ac0b Guido Trotter

1675 b330ac0b Guido Trotter
  Any errors are ignored.
1676 b330ac0b Guido Trotter

1677 58885d79 Iustin Pop
  @type name: str
1678 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1679 58885d79 Iustin Pop

1680 b330ac0b Guido Trotter
  """
1681 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1682 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1683 b330ac0b Guido Trotter
  try:
1684 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1685 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1686 b330ac0b Guido Trotter
    pass
1687 b330ac0b Guido Trotter
1688 b330ac0b Guido Trotter
1689 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1690 ff5251bc Iustin Pop
                waitpid=False):
1691 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1692 b2a1f511 Iustin Pop

1693 b2a1f511 Iustin Pop
  @type pid: int
1694 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1695 38206f3c Iustin Pop
  @type signal_: int
1696 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1697 b2a1f511 Iustin Pop
  @type timeout: int
1698 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1699 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1700 b2a1f511 Iustin Pop
                  will be done
1701 ff5251bc Iustin Pop
  @type waitpid: boolean
1702 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1703 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1704 ff5251bc Iustin Pop
      would remain as zombie
1705 b2a1f511 Iustin Pop

1706 b2a1f511 Iustin Pop
  """
1707 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1708 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1709 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1710 ff5251bc Iustin Pop
    if wait:
1711 ff5251bc Iustin Pop
      try:
1712 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1713 ff5251bc Iustin Pop
      except OSError:
1714 ff5251bc Iustin Pop
        pass
1715 ff5251bc Iustin Pop
1716 b2a1f511 Iustin Pop
  if pid <= 0:
1717 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1718 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1719 b2a1f511 Iustin Pop
1720 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1721 b2a1f511 Iustin Pop
    return
1722 31892b4c Michael Hanselmann
1723 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1724 31892b4c Michael Hanselmann
1725 b2a1f511 Iustin Pop
  if timeout <= 0:
1726 b2a1f511 Iustin Pop
    return
1727 7167159a Michael Hanselmann
1728 31892b4c Michael Hanselmann
  def _CheckProcess():
1729 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1730 31892b4c Michael Hanselmann
      return
1731 31892b4c Michael Hanselmann
1732 7167159a Michael Hanselmann
    try:
1733 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1734 7167159a Michael Hanselmann
    except OSError:
1735 31892b4c Michael Hanselmann
      raise RetryAgain()
1736 31892b4c Michael Hanselmann
1737 31892b4c Michael Hanselmann
    if result_pid > 0:
1738 31892b4c Michael Hanselmann
      return
1739 31892b4c Michael Hanselmann
1740 31892b4c Michael Hanselmann
    raise RetryAgain()
1741 31892b4c Michael Hanselmann
1742 31892b4c Michael Hanselmann
  try:
1743 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1744 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1745 31892b4c Michael Hanselmann
  except RetryTimeout:
1746 31892b4c Michael Hanselmann
    pass
1747 7167159a Michael Hanselmann
1748 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1749 7167159a Michael Hanselmann
    # Kill process if it's still alive
1750 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1751 b2a1f511 Iustin Pop
1752 b2a1f511 Iustin Pop
1753 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1754 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1755 57c177af Iustin Pop

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

1759 58885d79 Iustin Pop
  @type name: str
1760 58885d79 Iustin Pop
  @param name: the name to look for
1761 58885d79 Iustin Pop
  @type search_path: str
1762 58885d79 Iustin Pop
  @param search_path: location to start at
1763 58885d79 Iustin Pop
  @type test: callable
1764 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1765 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1766 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1767 58885d79 Iustin Pop
  @rtype: str or None
1768 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1769 57c177af Iustin Pop

1770 57c177af Iustin Pop
  """
1771 f95c81bf Iustin Pop
  # validate the filename mask
1772 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1773 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1774 f95c81bf Iustin Pop
                     name)
1775 f95c81bf Iustin Pop
    return None
1776 f95c81bf Iustin Pop
1777 57c177af Iustin Pop
  for dir_name in search_path:
1778 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1779 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1780 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1781 f95c81bf Iustin Pop
    # basename
1782 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1783 57c177af Iustin Pop
      return item_name
1784 57c177af Iustin Pop
  return None
1785 8d1a2a64 Michael Hanselmann
1786 8d1a2a64 Michael Hanselmann
1787 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1788 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1789 8d1a2a64 Michael Hanselmann

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

1793 58885d79 Iustin Pop
  @type vglist: dict
1794 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1795 58885d79 Iustin Pop
  @type vgname: str
1796 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1797 58885d79 Iustin Pop
  @type minsize: int
1798 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1799 58885d79 Iustin Pop
  @rtype: None or str
1800 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1801 8d1a2a64 Michael Hanselmann

1802 8d1a2a64 Michael Hanselmann
  """
1803 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1804 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1805 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1806 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1807 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1808 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1809 8d1a2a64 Michael Hanselmann
  return None
1810 7996a135 Iustin Pop
1811 7996a135 Iustin Pop
1812 45bc5e4a Michael Hanselmann
def SplitTime(value):
1813 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1814 739be818 Michael Hanselmann

1815 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1816 45bc5e4a Michael Hanselmann
  @type value: int or float
1817 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1818 739be818 Michael Hanselmann

1819 739be818 Michael Hanselmann
  """
1820 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1821 45bc5e4a Michael Hanselmann
1822 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1823 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1824 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1825 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1826 45bc5e4a Michael Hanselmann
1827 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1828 739be818 Michael Hanselmann
1829 739be818 Michael Hanselmann
1830 739be818 Michael Hanselmann
def MergeTime(timetuple):
1831 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1832 739be818 Michael Hanselmann

1833 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1834 739be818 Michael Hanselmann
  @type timetuple: tuple
1835 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1836 739be818 Michael Hanselmann

1837 739be818 Michael Hanselmann
  """
1838 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1839 739be818 Michael Hanselmann
1840 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1841 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1842 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1843 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1844 739be818 Michael Hanselmann
1845 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1846 739be818 Michael Hanselmann
1847 739be818 Michael Hanselmann
1848 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1849 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1850 4a8b186a Michael Hanselmann

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

1855 cd50653c Guido Trotter
  @type daemon_name: string
1856 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1857 58885d79 Iustin Pop
  @rtype: int
1858 58885d79 Iustin Pop

1859 4a8b186a Michael Hanselmann
  """
1860 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1861 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1862 cd50653c Guido Trotter
1863 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1864 4a8b186a Michael Hanselmann
  try:
1865 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1866 4a8b186a Michael Hanselmann
  except socket.error:
1867 cd50653c Guido Trotter
    port = default_port
1868 4a8b186a Michael Hanselmann
1869 4a8b186a Michael Hanselmann
  return port
1870 4a8b186a Michael Hanselmann
1871 4a8b186a Michael Hanselmann
1872 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
1873 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
1874 82d9caef Iustin Pop
  """Configures the logging module.
1875 82d9caef Iustin Pop

1876 58885d79 Iustin Pop
  @type logfile: str
1877 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1878 ea34193f Iustin Pop
  @type debug: integer
1879 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
1880 58885d79 Iustin Pop
      only those at C{INFO} and above level
1881 58885d79 Iustin Pop
  @type stderr_logging: boolean
1882 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1883 58885d79 Iustin Pop
  @type program: str
1884 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1885 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1886 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1887 551b6283 Iustin Pop
  @type syslog: string
1888 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
1889 551b6283 Iustin Pop
      - if no, syslog is not used
1890 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
1891 551b6283 Iustin Pop
      - if only, only syslog is used
1892 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1893 551b6283 Iustin Pop
      syslog/stderr logging is disabled
1894 58885d79 Iustin Pop

1895 82d9caef Iustin Pop
  """
1896 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1897 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
1898 d21d09d6 Iustin Pop
  if multithreaded:
1899 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1900 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
1901 82d9caef Iustin Pop
  if debug:
1902 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1903 551b6283 Iustin Pop
    # no debug info for syslog loggers
1904 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1905 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
1906 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
1907 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
1908 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1909 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
1910 82d9caef Iustin Pop
1911 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1912 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1913 82d9caef Iustin Pop
1914 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1915 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1916 7d88772a Iustin Pop
    handler.close()
1917 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1918 6346a9e5 Michael Hanselmann
1919 82d9caef Iustin Pop
  if stderr_logging:
1920 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1921 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1922 82d9caef Iustin Pop
    if debug:
1923 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1924 82d9caef Iustin Pop
    else:
1925 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1926 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1927 82d9caef Iustin Pop
1928 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
1929 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
1930 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
1931 551b6283 Iustin Pop
                                                    facility)
1932 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
1933 551b6283 Iustin Pop
    # Never enable debug over syslog
1934 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
1935 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
1936 551b6283 Iustin Pop
1937 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
1938 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
1939 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
1940 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
1941 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
1942 551b6283 Iustin Pop
    try:
1943 551b6283 Iustin Pop
      logfile_handler = logging.FileHandler(logfile)
1944 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
1945 551b6283 Iustin Pop
      if debug:
1946 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
1947 551b6283 Iustin Pop
      else:
1948 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
1949 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
1950 551b6283 Iustin Pop
    except EnvironmentError:
1951 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
1952 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
1953 551b6283 Iustin Pop
      else:
1954 551b6283 Iustin Pop
        # we need to re-raise the exception
1955 551b6283 Iustin Pop
        raise
1956 82d9caef Iustin Pop
1957 016d04b3 Michael Hanselmann
1958 da961187 Guido Trotter
def IsNormAbsPath(path):
1959 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
1960 da961187 Guido Trotter

1961 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1962 da961187 Guido Trotter

1963 da961187 Guido Trotter
  """
1964 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1965 82d9caef Iustin Pop
1966 016d04b3 Michael Hanselmann
1967 4bb678e9 Iustin Pop
def PathJoin(*args):
1968 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
1969 4bb678e9 Iustin Pop

1970 4bb678e9 Iustin Pop
  Requirements:
1971 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
1972 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
1973 4bb678e9 Iustin Pop
        since we check for normalization at the end
1974 4bb678e9 Iustin Pop

1975 4bb678e9 Iustin Pop
  @param args: the path components to be joined
1976 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
1977 4bb678e9 Iustin Pop

1978 4bb678e9 Iustin Pop
  """
1979 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
1980 4bb678e9 Iustin Pop
  assert args
1981 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
1982 4bb678e9 Iustin Pop
  root = args[0]
1983 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
1984 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
1985 4bb678e9 Iustin Pop
  result = os.path.join(*args)
1986 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
1987 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
1988 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
1989 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
1990 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
1991 4bb678e9 Iustin Pop
  if prefix != root:
1992 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
1993 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
1994 4bb678e9 Iustin Pop
  return result
1995 4bb678e9 Iustin Pop
1996 4bb678e9 Iustin Pop
1997 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1998 f65f63ef Iustin Pop
  """Return the last lines from a file.
1999 f65f63ef Iustin Pop

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

2004 f65f63ef Iustin Pop
  @param fname: the file name
2005 f65f63ef Iustin Pop
  @type lines: int
2006 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2007 f65f63ef Iustin Pop

2008 f65f63ef Iustin Pop
  """
2009 f65f63ef Iustin Pop
  fd = open(fname, "r")
2010 f65f63ef Iustin Pop
  try:
2011 f65f63ef Iustin Pop
    fd.seek(0, 2)
2012 f65f63ef Iustin Pop
    pos = fd.tell()
2013 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2014 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2015 f65f63ef Iustin Pop
    raw_data = fd.read()
2016 f65f63ef Iustin Pop
  finally:
2017 f65f63ef Iustin Pop
    fd.close()
2018 f65f63ef Iustin Pop
2019 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2020 f65f63ef Iustin Pop
  return rows[-lines:]
2021 f65f63ef Iustin Pop
2022 f65f63ef Iustin Pop
2023 26f15862 Iustin Pop
def SafeEncode(text):
2024 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2025 26f15862 Iustin Pop

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

2035 26f15862 Iustin Pop
  @type text: str or unicode
2036 26f15862 Iustin Pop
  @param text: input data
2037 26f15862 Iustin Pop
  @rtype: str
2038 26f15862 Iustin Pop
  @return: a safe version of text
2039 26f15862 Iustin Pop

2040 26f15862 Iustin Pop
  """
2041 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2042 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2043 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2044 d392fa34 Iustin Pop
  resu = ""
2045 d392fa34 Iustin Pop
  for char in text:
2046 d392fa34 Iustin Pop
    c = ord(char)
2047 d392fa34 Iustin Pop
    if char  == '\t':
2048 d392fa34 Iustin Pop
      resu += r'\t'
2049 d392fa34 Iustin Pop
    elif char == '\n':
2050 d392fa34 Iustin Pop
      resu += r'\n'
2051 d392fa34 Iustin Pop
    elif char == '\r':
2052 d392fa34 Iustin Pop
      resu += r'\'r'
2053 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2054 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2055 d392fa34 Iustin Pop
    else:
2056 d392fa34 Iustin Pop
      resu += char
2057 d392fa34 Iustin Pop
  return resu
2058 26f15862 Iustin Pop
2059 26f15862 Iustin Pop
2060 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2061 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2062 5b69bc7c Iustin Pop

2063 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2064 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2065 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2066 5b69bc7c Iustin Pop
  separator):
2067 5b69bc7c Iustin Pop
    - a plain , separates the elements
2068 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2069 5b69bc7c Iustin Pop
      backslash plus a separator comma
2070 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2071 5b69bc7c Iustin Pop
      non-separator comma
2072 5b69bc7c Iustin Pop

2073 5b69bc7c Iustin Pop
  @type text: string
2074 5b69bc7c Iustin Pop
  @param text: the string to split
2075 5b69bc7c Iustin Pop
  @type sep: string
2076 5b69bc7c Iustin Pop
  @param text: the separator
2077 5b69bc7c Iustin Pop
  @rtype: string
2078 5b69bc7c Iustin Pop
  @return: a list of strings
2079 5b69bc7c Iustin Pop

2080 5b69bc7c Iustin Pop
  """
2081 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2082 5b69bc7c Iustin Pop
  slist = text.split(sep)
2083 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2084 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2085 5b69bc7c Iustin Pop
  rlist = []
2086 5b69bc7c Iustin Pop
  while slist:
2087 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2088 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2089 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2090 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2091 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2092 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2093 5b69bc7c Iustin Pop
        # the next step
2094 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2095 5b69bc7c Iustin Pop
        continue
2096 5b69bc7c Iustin Pop
    rlist.append(e1)
2097 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2098 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2099 5b69bc7c Iustin Pop
  return rlist
2100 5b69bc7c Iustin Pop
2101 5b69bc7c Iustin Pop
2102 ab3e6da8 Iustin Pop
def CommaJoin(names):
2103 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2104 ab3e6da8 Iustin Pop

2105 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2106 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2107 ab3e6da8 Iustin Pop

2108 ab3e6da8 Iustin Pop
  """
2109 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2110 ab3e6da8 Iustin Pop
2111 ab3e6da8 Iustin Pop
2112 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2113 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2114 3f6a47a8 Michael Hanselmann

2115 3f6a47a8 Michael Hanselmann
  @type value: int
2116 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2117 3f6a47a8 Michael Hanselmann
  @rtype: int
2118 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2119 3f6a47a8 Michael Hanselmann

2120 3f6a47a8 Michael Hanselmann
  """
2121 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2122 3f6a47a8 Michael Hanselmann
2123 3f6a47a8 Michael Hanselmann
2124 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2125 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2126 3f6a47a8 Michael Hanselmann

2127 3f6a47a8 Michael Hanselmann
  @type path: string
2128 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2129 3f6a47a8 Michael Hanselmann
  @rtype: int
2130 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2131 3f6a47a8 Michael Hanselmann

2132 3f6a47a8 Michael Hanselmann
  """
2133 3f6a47a8 Michael Hanselmann
  size = 0
2134 3f6a47a8 Michael Hanselmann
2135 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2136 2a887df9 Michael Hanselmann
    for filename in files:
2137 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2138 3f6a47a8 Michael Hanselmann
      size += st.st_size
2139 3f6a47a8 Michael Hanselmann
2140 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2141 3f6a47a8 Michael Hanselmann
2142 3f6a47a8 Michael Hanselmann
2143 620a85fd Iustin Pop
def GetFilesystemStats(path):
2144 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2145 3f6a47a8 Michael Hanselmann

2146 3f6a47a8 Michael Hanselmann
  @type path: string
2147 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2148 3f6a47a8 Michael Hanselmann
  @rtype: int
2149 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2150 3f6a47a8 Michael Hanselmann

2151 3f6a47a8 Michael Hanselmann
  """
2152 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2153 3f6a47a8 Michael Hanselmann
2154 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2155 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2156 620a85fd Iustin Pop
  return (tsize, fsize)
2157 3f6a47a8 Michael Hanselmann
2158 3f6a47a8 Michael Hanselmann
2159 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2160 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2161 eb58f7bd Michael Hanselmann

2162 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2163 eb58f7bd Michael Hanselmann

2164 eb58f7bd Michael Hanselmann
  @type fn: callable
2165 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2166 bdefe5dd Michael Hanselmann
  @rtype: bool
2167 bdefe5dd Michael Hanselmann
  @return: Function's result
2168 eb58f7bd Michael Hanselmann

2169 eb58f7bd Michael Hanselmann
  """
2170 eb58f7bd Michael Hanselmann
  pid = os.fork()
2171 eb58f7bd Michael Hanselmann
  if pid == 0:
2172 eb58f7bd Michael Hanselmann
    # Child process
2173 eb58f7bd Michael Hanselmann
    try:
2174 82869978 Michael Hanselmann
      # In case the function uses temporary files
2175 82869978 Michael Hanselmann
      ResetTempfileModule()
2176 82869978 Michael Hanselmann
2177 eb58f7bd Michael Hanselmann
      # Call function
2178 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2179 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2180 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2181 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2182 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2183 eb58f7bd Michael Hanselmann
      result = 33
2184 eb58f7bd Michael Hanselmann
2185 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2186 eb58f7bd Michael Hanselmann
2187 eb58f7bd Michael Hanselmann
  # Parent process
2188 eb58f7bd Michael Hanselmann
2189 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2190 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2191 eb58f7bd Michael Hanselmann
2192 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2193 eb58f7bd Michael Hanselmann
    exitcode = None
2194 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2195 eb58f7bd Michael Hanselmann
  else:
2196 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2197 eb58f7bd Michael Hanselmann
    signum = None
2198 eb58f7bd Michael Hanselmann
2199 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2200 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2201 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2202 eb58f7bd Michael Hanselmann
2203 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2204 eb58f7bd Michael Hanselmann
2205 eb58f7bd Michael Hanselmann
2206 7996a135 Iustin Pop
def LockedMethod(fn):
2207 7996a135 Iustin Pop
  """Synchronized object access decorator.
2208 7996a135 Iustin Pop

2209 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2210 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2211 7996a135 Iustin Pop

2212 7996a135 Iustin Pop
  """
2213 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2214 e67bd559 Michael Hanselmann
    if debug_locks:
2215 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2216 e67bd559 Michael Hanselmann
2217 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2218 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2219 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2220 7996a135 Iustin Pop
    lock = self._lock
2221 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2222 7996a135 Iustin Pop
    lock.acquire()
2223 7996a135 Iustin Pop
    try:
2224 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2225 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2226 7996a135 Iustin Pop
    finally:
2227 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2228 7996a135 Iustin Pop
      lock.release()
2229 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2230 7996a135 Iustin Pop
    return result
2231 7996a135 Iustin Pop
  return wrapper
2232 eb0f0ce0 Michael Hanselmann
2233 eb0f0ce0 Michael Hanselmann
2234 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2235 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2236 eb0f0ce0 Michael Hanselmann

2237 58885d79 Iustin Pop
  @type fd: int
2238 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2239 58885d79 Iustin Pop

2240 eb0f0ce0 Michael Hanselmann
  """
2241 eb0f0ce0 Michael Hanselmann
  try:
2242 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2243 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2244 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2245 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2246 eb0f0ce0 Michael Hanselmann
    raise
2247 de499029 Michael Hanselmann
2248 de499029 Michael Hanselmann
2249 3b813dd2 Iustin Pop
def FormatTime(val):
2250 3b813dd2 Iustin Pop
  """Formats a time value.
2251 3b813dd2 Iustin Pop

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

2256 3b813dd2 Iustin Pop
  """
2257 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2258 3b813dd2 Iustin Pop
    return "N/A"
2259 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2260 3b813dd2 Iustin Pop
  # platforms
2261 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2262 3b813dd2 Iustin Pop
2263 3b813dd2 Iustin Pop
2264 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2265 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2266 05e50653 Michael Hanselmann

2267 5cbe43a5 Michael Hanselmann
  @type filename: string
2268 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2269 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2270 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2271 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2272 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2273 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2274 5cbe43a5 Michael Hanselmann

2275 05e50653 Michael Hanselmann
  """
2276 05e50653 Michael Hanselmann
  if now is None:
2277 05e50653 Michael Hanselmann
    now = time.time()
2278 05e50653 Michael Hanselmann
2279 05e50653 Michael Hanselmann
  try:
2280 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2281 05e50653 Michael Hanselmann
  except IOError, err:
2282 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2283 05e50653 Michael Hanselmann
      raise
2284 05e50653 Michael Hanselmann
    value = None
2285 05e50653 Michael Hanselmann
2286 05e50653 Michael Hanselmann
  if value is not None:
2287 05e50653 Michael Hanselmann
    try:
2288 05e50653 Michael Hanselmann
      value = int(value)
2289 05e50653 Michael Hanselmann
    except ValueError:
2290 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2291 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2292 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2293 05e50653 Michael Hanselmann
      value = None
2294 05e50653 Michael Hanselmann
2295 05e50653 Michael Hanselmann
    if value is not None:
2296 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2297 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2298 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2299 5cbe43a5 Michael Hanselmann
        value = None
2300 5cbe43a5 Michael Hanselmann
2301 5cbe43a5 Michael Hanselmann
      elif now > value:
2302 05e50653 Michael Hanselmann
        value = None
2303 05e50653 Michael Hanselmann
2304 05e50653 Michael Hanselmann
  return value
2305 05e50653 Michael Hanselmann
2306 05e50653 Michael Hanselmann
2307 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2308 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2309 de0ea66b Michael Hanselmann

2310 de0ea66b Michael Hanselmann
  """
2311 de0ea66b Michael Hanselmann
2312 de0ea66b Michael Hanselmann
2313 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2314 de0ea66b Michael Hanselmann
  """Retry again.
2315 de0ea66b Michael Hanselmann

2316 de0ea66b Michael Hanselmann
  """
2317 de0ea66b Michael Hanselmann
2318 de0ea66b Michael Hanselmann
2319 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2320 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2321 de0ea66b Michael Hanselmann

2322 de0ea66b Michael Hanselmann
  """
2323 de0ea66b Michael Hanselmann
  __slots__ = [
2324 de0ea66b Michael Hanselmann
    "_factor",
2325 de0ea66b Michael Hanselmann
    "_limit",
2326 de0ea66b Michael Hanselmann
    "_next",
2327 de0ea66b Michael Hanselmann
    "_start",
2328 de0ea66b Michael Hanselmann
    ]
2329 de0ea66b Michael Hanselmann
2330 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2331 de0ea66b Michael Hanselmann
    """Initializes this class.
2332 de0ea66b Michael Hanselmann

2333 de0ea66b Michael Hanselmann
    @type start: float
2334 de0ea66b Michael Hanselmann
    @param start: Initial delay
2335 de0ea66b Michael Hanselmann
    @type factor: float
2336 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2337 de0ea66b Michael Hanselmann
    @type limit: float or None
2338 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2339 de0ea66b Michael Hanselmann

2340 de0ea66b Michael Hanselmann
    """
2341 de0ea66b Michael Hanselmann
    assert start > 0.0
2342 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2343 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2344 de0ea66b Michael Hanselmann
2345 de0ea66b Michael Hanselmann
    self._start = start
2346 de0ea66b Michael Hanselmann
    self._factor = factor
2347 de0ea66b Michael Hanselmann
    self._limit = limit
2348 de0ea66b Michael Hanselmann
2349 de0ea66b Michael Hanselmann
    self._next = start
2350 de0ea66b Michael Hanselmann
2351 de0ea66b Michael Hanselmann
  def __call__(self):
2352 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2353 de0ea66b Michael Hanselmann

2354 de0ea66b Michael Hanselmann
    """
2355 de0ea66b Michael Hanselmann
    current = self._next
2356 de0ea66b Michael Hanselmann
2357 de0ea66b Michael Hanselmann
    # Update for next run
2358 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2359 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2360 de0ea66b Michael Hanselmann
2361 de0ea66b Michael Hanselmann
    return current
2362 de0ea66b Michael Hanselmann
2363 de0ea66b Michael Hanselmann
2364 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2365 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2366 de0ea66b Michael Hanselmann
2367 de0ea66b Michael Hanselmann
2368 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2369 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2370 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2371 de0ea66b Michael Hanselmann

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

2376 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2377 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2378 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2379 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2380 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2381 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2382 de0ea66b Michael Hanselmann

2383 de0ea66b Michael Hanselmann
  @type fn: callable
2384 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2385 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2386 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2387 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2388 de0ea66b Michael Hanselmann
  @type timeout: float
2389 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2390 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2391 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2392 de0ea66b Michael Hanselmann
  @return: Return value of function
2393 de0ea66b Michael Hanselmann

2394 de0ea66b Michael Hanselmann
  """
2395 de0ea66b Michael Hanselmann
  assert callable(fn)
2396 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2397 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2398 de0ea66b Michael Hanselmann
2399 de0ea66b Michael Hanselmann
  if args is None:
2400 de0ea66b Michael Hanselmann
    args = []
2401 de0ea66b Michael Hanselmann
2402 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2403 de0ea66b Michael Hanselmann
2404 de0ea66b Michael Hanselmann
  if callable(delay):
2405 de0ea66b Michael Hanselmann
    # External function to calculate delay
2406 de0ea66b Michael Hanselmann
    calc_delay = delay
2407 de0ea66b Michael Hanselmann
2408 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2409 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2410 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2411 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2412 de0ea66b Michael Hanselmann
2413 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2414 de0ea66b Michael Hanselmann
    # Always use the remaining time
2415 de0ea66b Michael Hanselmann
    calc_delay = None
2416 de0ea66b Michael Hanselmann
2417 de0ea66b Michael Hanselmann
  else:
2418 de0ea66b Michael Hanselmann
    # Static delay
2419 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2420 de0ea66b Michael Hanselmann
2421 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2422 de0ea66b Michael Hanselmann
2423 de0ea66b Michael Hanselmann
  while True:
2424 de0ea66b Michael Hanselmann
    try:
2425 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2426 de0ea66b Michael Hanselmann
      return fn(*args)
2427 de0ea66b Michael Hanselmann
    except RetryAgain:
2428 de0ea66b Michael Hanselmann
      pass
2429 de0ea66b Michael Hanselmann
2430 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2431 de0ea66b Michael Hanselmann
2432 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2433 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2434 de0ea66b Michael Hanselmann
2435 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2436 de0ea66b Michael Hanselmann
2437 de0ea66b Michael Hanselmann
    if calc_delay is None:
2438 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2439 de0ea66b Michael Hanselmann
    else:
2440 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2441 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2442 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2443 de0ea66b Michael Hanselmann
2444 de0ea66b Michael Hanselmann
2445 a87b4824 Michael Hanselmann
class FileLock(object):
2446 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2447 a87b4824 Michael Hanselmann

2448 a87b4824 Michael Hanselmann
  """
2449 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2450 58885d79 Iustin Pop
    """Constructor for FileLock.
2451 58885d79 Iustin Pop

2452 b4478d34 Michael Hanselmann
    @type fd: file
2453 b4478d34 Michael Hanselmann
    @param fd: File object
2454 58885d79 Iustin Pop
    @type filename: str
2455 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2456 58885d79 Iustin Pop

2457 58885d79 Iustin Pop
    """
2458 b4478d34 Michael Hanselmann
    self.fd = fd
2459 a87b4824 Michael Hanselmann
    self.filename = filename
2460 b4478d34 Michael Hanselmann
2461 b4478d34 Michael Hanselmann
  @classmethod
2462 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2463 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2464 b4478d34 Michael Hanselmann

2465 b4478d34 Michael Hanselmann
    @type filename: string
2466 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2467 b4478d34 Michael Hanselmann

2468 b4478d34 Michael Hanselmann
    """
2469 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2470 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2471 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2472 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2473 b4478d34 Michael Hanselmann
               filename)
2474 a87b4824 Michael Hanselmann
2475 a87b4824 Michael Hanselmann
  def __del__(self):
2476 a87b4824 Michael Hanselmann
    self.Close()
2477 a87b4824 Michael Hanselmann
2478 a87b4824 Michael Hanselmann
  def Close(self):
2479 58885d79 Iustin Pop
    """Close the file and release the lock.
2480 58885d79 Iustin Pop

2481 58885d79 Iustin Pop
    """
2482 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2483 a87b4824 Michael Hanselmann
      self.fd.close()
2484 a87b4824 Michael Hanselmann
      self.fd = None
2485 a87b4824 Michael Hanselmann
2486 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2487 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2488 aa74b828 Michael Hanselmann

2489 aa74b828 Michael Hanselmann
    @type flag: int
2490 58885d79 Iustin Pop
    @param flag: operation flag
2491 aa74b828 Michael Hanselmann
    @type blocking: bool
2492 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2493 aa74b828 Michael Hanselmann
    @type timeout: None or float
2494 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2495 aa74b828 Michael Hanselmann
                    non-blocking mode).
2496 aa74b828 Michael Hanselmann
    @type errmsg: string
2497 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2498 aa74b828 Michael Hanselmann

2499 aa74b828 Michael Hanselmann
    """
2500 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2501 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2502 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2503 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2504 a87b4824 Michael Hanselmann
2505 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2506 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2507 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2508 a87b4824 Michael Hanselmann
2509 cc4c9b91 Michael Hanselmann
    if timeout is None:
2510 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2511 cc4c9b91 Michael Hanselmann
    else:
2512 cc4c9b91 Michael Hanselmann
      try:
2513 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2514 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2515 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2516 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2517 aa74b828 Michael Hanselmann
2518 cc4c9b91 Michael Hanselmann
  @staticmethod
2519 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2520 cc4c9b91 Michael Hanselmann
    try:
2521 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2522 cc4c9b91 Michael Hanselmann
    except IOError, err:
2523 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2524 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2525 31892b4c Michael Hanselmann
2526 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2527 cc4c9b91 Michael Hanselmann
      raise
2528 aa74b828 Michael Hanselmann
2529 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2530 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2531 a87b4824 Michael Hanselmann

2532 58885d79 Iustin Pop
    @type blocking: boolean
2533 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2534 58885d79 Iustin Pop
        can lock the file or return immediately
2535 58885d79 Iustin Pop
    @type timeout: int or None
2536 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2537 58885d79 Iustin Pop
        (in blocking mode)
2538 58885d79 Iustin Pop

2539 a87b4824 Michael Hanselmann
    """
2540 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2541 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2542 a87b4824 Michael Hanselmann
2543 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2544 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2545 a87b4824 Michael Hanselmann

2546 58885d79 Iustin Pop
    @type blocking: boolean
2547 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2548 58885d79 Iustin Pop
        can lock the file or return immediately
2549 58885d79 Iustin Pop
    @type timeout: int or None
2550 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2551 58885d79 Iustin Pop
        (in blocking mode)
2552 58885d79 Iustin Pop

2553 a87b4824 Michael Hanselmann
    """
2554 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2555 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2556 a87b4824 Michael Hanselmann
2557 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2558 a87b4824 Michael Hanselmann
    """Unlocks the file.
2559 a87b4824 Michael Hanselmann

2560 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2561 58885d79 Iustin Pop
    operation::
2562 58885d79 Iustin Pop

2563 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2564 58885d79 Iustin Pop
      operations.
2565 58885d79 Iustin Pop

2566 58885d79 Iustin Pop
    @type blocking: boolean
2567 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2568 58885d79 Iustin Pop
        can lock the file or return immediately
2569 58885d79 Iustin Pop
    @type timeout: int or None
2570 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2571 58885d79 Iustin Pop
        (in blocking mode)
2572 a87b4824 Michael Hanselmann

2573 a87b4824 Michael Hanselmann
    """
2574 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2575 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2576 a87b4824 Michael Hanselmann
2577 a87b4824 Michael Hanselmann
2578 451575de Guido Trotter
def SignalHandled(signums):
2579 451575de Guido Trotter
  """Signal Handled decoration.
2580 451575de Guido Trotter

2581 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2582 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2583 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2584 451575de Guido Trotter
  objects as values.
2585 451575de Guido Trotter

2586 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2587 451575de Guido Trotter
  with different handlers.
2588 451575de Guido Trotter

2589 451575de Guido Trotter
  @type signums: list
2590 451575de Guido Trotter
  @param signums: signals to intercept
2591 451575de Guido Trotter

2592 451575de Guido Trotter
  """
2593 451575de Guido Trotter
  def wrap(fn):
2594 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2595 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2596 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2597 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2598 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2599 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2600 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2601 451575de Guido Trotter
      else:
2602 451575de Guido Trotter
        signal_handlers = {}
2603 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2604 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2605 451575de Guido Trotter
      try:
2606 451575de Guido Trotter
        for sig in signums:
2607 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2608 451575de Guido Trotter
        return fn(*args, **kwargs)
2609 451575de Guido Trotter
      finally:
2610 451575de Guido Trotter
        sighandler.Reset()
2611 451575de Guido Trotter
    return sig_function
2612 451575de Guido Trotter
  return wrap
2613 451575de Guido Trotter
2614 451575de Guido Trotter
2615 de499029 Michael Hanselmann
class SignalHandler(object):
2616 de499029 Michael Hanselmann
  """Generic signal handler class.
2617 de499029 Michael Hanselmann

2618 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2619 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2620 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2621 58885d79 Iustin Pop
  signal was sent.
2622 58885d79 Iustin Pop

2623 58885d79 Iustin Pop
  @type signum: list
2624 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2625 58885d79 Iustin Pop
  @type called: boolean
2626 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2627 de499029 Michael Hanselmann

2628 de499029 Michael Hanselmann
  """
2629 de499029 Michael Hanselmann
  def __init__(self, signum):
2630 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2631 de499029 Michael Hanselmann

2632 58885d79 Iustin Pop
    @type signum: int or list of ints
2633 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2634 de499029 Michael Hanselmann

2635 de499029 Michael Hanselmann
    """
2636 6c52849e Guido Trotter
    self.signum = set(signum)
2637 de499029 Michael Hanselmann
    self.called = False
2638 de499029 Michael Hanselmann
2639 de499029 Michael Hanselmann
    self._previous = {}
2640 de499029 Michael Hanselmann
    try:
2641 de499029 Michael Hanselmann
      for signum in self.signum:
2642 de499029 Michael Hanselmann
        # Setup handler
2643 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2644 de499029 Michael Hanselmann
        try:
2645 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2646 de499029 Michael Hanselmann
        except:
2647 de499029 Michael Hanselmann
          # Restore previous handler
2648 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2649 de499029 Michael Hanselmann
          raise
2650 de499029 Michael Hanselmann
    except:
2651 de499029 Michael Hanselmann
      # Reset all handlers
2652 de499029 Michael Hanselmann
      self.Reset()
2653 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2654 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2655 de499029 Michael Hanselmann
      raise
2656 de499029 Michael Hanselmann
2657 de499029 Michael Hanselmann
  def __del__(self):
2658 de499029 Michael Hanselmann
    self.Reset()
2659 de499029 Michael Hanselmann
2660 de499029 Michael Hanselmann
  def Reset(self):
2661 de499029 Michael Hanselmann
    """Restore previous handler.
2662 de499029 Michael Hanselmann

2663 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2664 58885d79 Iustin Pop

2665 de499029 Michael Hanselmann
    """
2666 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2667 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2668 de499029 Michael Hanselmann
      # If successful, remove from dict
2669 de499029 Michael Hanselmann
      del self._previous[signum]
2670 de499029 Michael Hanselmann
2671 de499029 Michael Hanselmann
  def Clear(self):
2672 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2673 de499029 Michael Hanselmann

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

2676 de499029 Michael Hanselmann
    """
2677 de499029 Michael Hanselmann
    self.called = False
2678 de499029 Michael Hanselmann
2679 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2680 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2681 de499029 Michael Hanselmann
    """Actual signal handling function.
2682 de499029 Michael Hanselmann

2683 de499029 Michael Hanselmann
    """
2684 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2685 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2686 de499029 Michael Hanselmann
    self.called = True
2687 a2d2e1a7 Iustin Pop
2688 a2d2e1a7 Iustin Pop
2689 a2d2e1a7 Iustin Pop
class FieldSet(object):
2690 a2d2e1a7 Iustin Pop
  """A simple field set.
2691 a2d2e1a7 Iustin Pop

2692 a2d2e1a7 Iustin Pop
  Among the features are:
2693 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2694 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2695 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2696 a2d2e1a7 Iustin Pop

2697 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2698 a2d2e1a7 Iustin Pop

2699 a2d2e1a7 Iustin Pop
  """
2700 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2701 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2702 a2d2e1a7 Iustin Pop
2703 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2704 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2705 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2706 a2d2e1a7 Iustin Pop
2707 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2708 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2709 a2d2e1a7 Iustin Pop

2710 a2d2e1a7 Iustin Pop
    @type field: str
2711 a2d2e1a7 Iustin Pop
    @param field: the string to match
2712 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2713 a2d2e1a7 Iustin Pop

2714 a2d2e1a7 Iustin Pop
    """
2715 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2716 a2d2e1a7 Iustin Pop
      return m
2717 6c881c52 Iustin Pop
    return None
2718 a2d2e1a7 Iustin Pop
2719 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2720 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2721 a2d2e1a7 Iustin Pop

2722 a2d2e1a7 Iustin Pop
    @type items: list
2723 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2724 a2d2e1a7 Iustin Pop
    @rtype: list
2725 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2726 a2d2e1a7 Iustin Pop

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