Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 728489a3

History | View | Annotate | Download (77.9 kB)

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

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

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import os
31 a8083063 Iustin Pop
import time
32 113b55aa Iustin Pop
import subprocess
33 a8083063 Iustin Pop
import re
34 a8083063 Iustin Pop
import socket
35 a8083063 Iustin Pop
import tempfile
36 a8083063 Iustin Pop
import shutil
37 4ca1b175 Alexander Schreiber
import errno
38 2f8b60b3 Iustin Pop
import pwd
39 78feb6fb Guido Trotter
import itertools
40 9c233417 Iustin Pop
import select
41 9c233417 Iustin Pop
import fcntl
42 8f765069 Iustin Pop
import resource
43 bb698c1f Iustin Pop
import logging
44 551b6283 Iustin Pop
import logging.handlers
45 de499029 Michael Hanselmann
import signal
46 27e46076 Michael Hanselmann
import datetime
47 27e46076 Michael Hanselmann
import calendar
48 9c233417 Iustin Pop
49 9c233417 Iustin Pop
from cStringIO import StringIO
50 a8083063 Iustin Pop
51 7ffe8fba Carlos Valiente
try:
52 7ffe8fba Carlos Valiente
  from hashlib import sha1
53 7ffe8fba Carlos Valiente
except ImportError:
54 7ffe8fba Carlos Valiente
  import sha
55 7ffe8fba Carlos Valiente
  sha1 = sha.new
56 7ffe8fba Carlos Valiente
57 a8083063 Iustin Pop
from ganeti import errors
58 3aecd2c7 Iustin Pop
from ganeti import constants
59 a8083063 Iustin Pop
60 16abfbc2 Alexander Schreiber
61 a8083063 Iustin Pop
_locksheld = []
62 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
63 a8083063 Iustin Pop
64 e67bd559 Michael Hanselmann
debug_locks = False
65 58885d79 Iustin Pop
66 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
67 b74159ee Iustin Pop
no_fork = False
68 f362096f Iustin Pop
69 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
70 13998ef2 Michael Hanselmann
71 7c0d6283 Michael Hanselmann
72 a8083063 Iustin Pop
class RunResult(object):
73 58885d79 Iustin Pop
  """Holds the result of running external programs.
74 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

619 bcf043c9 Iustin Pop
  """
620 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
621 26288e68 Iustin Pop
622 89e1fc26 Iustin Pop
  def __init__(self, name=None):
623 bcf043c9 Iustin Pop
    """Initialize the host name object.
624 bcf043c9 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1116 d9c02ca6 Michael Hanselmann
  """
1117 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1118 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1119 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1120 d9c02ca6 Michael Hanselmann
1121 d9c02ca6 Michael Hanselmann
1122 1d466a4f Michael Hanselmann
def TimestampForFilename():
1123 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1124 1d466a4f Michael Hanselmann

1125 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1126 1d466a4f Michael Hanselmann
  separators.
1127 1d466a4f Michael Hanselmann

1128 1d466a4f Michael Hanselmann
  """
1129 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1130 1d466a4f Michael Hanselmann
1131 1d466a4f Michael Hanselmann
1132 a8083063 Iustin Pop
def CreateBackup(file_name):
1133 a8083063 Iustin Pop
  """Creates a backup of a file.
1134 a8083063 Iustin Pop

1135 58885d79 Iustin Pop
  @type file_name: str
1136 58885d79 Iustin Pop
  @param file_name: file to be backed up
1137 58885d79 Iustin Pop
  @rtype: str
1138 58885d79 Iustin Pop
  @return: the path to the newly created backup
1139 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1140 a8083063 Iustin Pop

1141 a8083063 Iustin Pop
  """
1142 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1143 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1144 3ecf6786 Iustin Pop
                                file_name)
1145 a8083063 Iustin Pop
1146 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1147 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1148 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1149 081b1e69 Michael Hanselmann
1150 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1151 081b1e69 Michael Hanselmann
  try:
1152 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1153 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1154 081b1e69 Michael Hanselmann
    try:
1155 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1156 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1157 081b1e69 Michael Hanselmann
    finally:
1158 081b1e69 Michael Hanselmann
      fdst.close()
1159 081b1e69 Michael Hanselmann
  finally:
1160 081b1e69 Michael Hanselmann
    fsrc.close()
1161 081b1e69 Michael Hanselmann
1162 a8083063 Iustin Pop
  return backup_name
1163 a8083063 Iustin Pop
1164 a8083063 Iustin Pop
1165 a8083063 Iustin Pop
def ShellQuote(value):
1166 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1167 3ecf6786 Iustin Pop

1168 58885d79 Iustin Pop
  @type value: str
1169 58885d79 Iustin Pop
  @param value: the argument to be quoted
1170 58885d79 Iustin Pop
  @rtype: str
1171 58885d79 Iustin Pop
  @return: the quoted value
1172 58885d79 Iustin Pop

1173 a8083063 Iustin Pop
  """
1174 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1175 a8083063 Iustin Pop
    return value
1176 a8083063 Iustin Pop
  else:
1177 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1178 a8083063 Iustin Pop
1179 a8083063 Iustin Pop
1180 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1181 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1182 58885d79 Iustin Pop

1183 58885d79 Iustin Pop
  @type args: list
1184 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1185 58885d79 Iustin Pop
  @rtype: str
1186 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1187 a8083063 Iustin Pop

1188 a8083063 Iustin Pop
  """
1189 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1190 88d14415 Michael Hanselmann
1191 88d14415 Michael Hanselmann
1192 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1193 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1194 2c30e9d7 Alexander Schreiber

1195 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1196 58885d79 Iustin Pop
  to it.
1197 58885d79 Iustin Pop

1198 58885d79 Iustin Pop
  @type target: str
1199 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1200 58885d79 Iustin Pop
  @type port: int
1201 58885d79 Iustin Pop
  @param port: the port to connect to
1202 58885d79 Iustin Pop
  @type timeout: int
1203 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1204 58885d79 Iustin Pop
  @type live_port_needed: boolean
1205 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1206 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1207 58885d79 Iustin Pop
  @type source: str or None
1208 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1209 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1210 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1211 2c30e9d7 Alexander Schreiber

1212 2c30e9d7 Alexander Schreiber
  """
1213 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1214 2c30e9d7 Alexander Schreiber
1215 0b5ad33e Iustin Pop
  success = False
1216 2c30e9d7 Alexander Schreiber
1217 b15d625f Iustin Pop
  if source is not None:
1218 b15d625f Iustin Pop
    try:
1219 b15d625f Iustin Pop
      sock.bind((source, 0))
1220 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1221 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1222 b15d625f Iustin Pop
        success = False
1223 2c30e9d7 Alexander Schreiber
1224 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1225 2c30e9d7 Alexander Schreiber
1226 2c30e9d7 Alexander Schreiber
  try:
1227 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1228 2c30e9d7 Alexander Schreiber
    sock.close()
1229 2c30e9d7 Alexander Schreiber
    success = True
1230 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1231 2c30e9d7 Alexander Schreiber
    success = False
1232 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1233 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1234 2c30e9d7 Alexander Schreiber
1235 2c30e9d7 Alexander Schreiber
  return success
1236 eedbda4b Michael Hanselmann
1237 eedbda4b Michael Hanselmann
1238 caad16e2 Iustin Pop
def OwnIpAddress(address):
1239 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1240 caad16e2 Iustin Pop

1241 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1242 caad16e2 Iustin Pop
  address.
1243 caad16e2 Iustin Pop

1244 caad16e2 Iustin Pop
  @type address: string
1245 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1246 caad16e2 Iustin Pop
  @rtype: bool
1247 58885d79 Iustin Pop
  @return: True if we own the address
1248 caad16e2 Iustin Pop

1249 caad16e2 Iustin Pop
  """
1250 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1251 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1252 caad16e2 Iustin Pop
1253 caad16e2 Iustin Pop
1254 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1255 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1256 58885d79 Iustin Pop

1257 58885d79 Iustin Pop
  @type path: str
1258 58885d79 Iustin Pop
  @param path: the directory to enumerate
1259 58885d79 Iustin Pop
  @rtype: list
1260 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1261 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1262 eedbda4b Michael Hanselmann

1263 eedbda4b Michael Hanselmann
  """
1264 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1265 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1266 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1267 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1268 f3299a07 Michael Hanselmann
  files.sort()
1269 f3299a07 Michael Hanselmann
  return files
1270 2f8b60b3 Iustin Pop
1271 2f8b60b3 Iustin Pop
1272 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1273 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1274 257f4c0a Iustin Pop

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

1279 2f8b60b3 Iustin Pop
  """
1280 2f8b60b3 Iustin Pop
  try:
1281 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1282 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1283 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1284 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1285 257f4c0a Iustin Pop
    else:
1286 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1287 257f4c0a Iustin Pop
                                   type(user))
1288 2f8b60b3 Iustin Pop
  except KeyError:
1289 2f8b60b3 Iustin Pop
    return default
1290 2f8b60b3 Iustin Pop
  return result.pw_dir
1291 59072e7e Michael Hanselmann
1292 59072e7e Michael Hanselmann
1293 24818e8f Michael Hanselmann
def NewUUID():
1294 59072e7e Michael Hanselmann
  """Returns a random UUID.
1295 59072e7e Michael Hanselmann

1296 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1297 58885d79 Iustin Pop
      filesystem.
1298 58885d79 Iustin Pop
  @rtype: str
1299 58885d79 Iustin Pop

1300 59072e7e Michael Hanselmann
  """
1301 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1302 087b34fe Iustin Pop
1303 087b34fe Iustin Pop
1304 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1305 33081d90 Iustin Pop
  """Generates a random secret.
1306 33081d90 Iustin Pop

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

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

1315 33081d90 Iustin Pop
  """
1316 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1317 33081d90 Iustin Pop
1318 33081d90 Iustin Pop
1319 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1320 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1321 9dae41ad Guido Trotter

1322 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1323 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1324 9dae41ad Guido Trotter

1325 9dae41ad Guido Trotter
  """
1326 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1327 9dae41ad Guido Trotter
    try:
1328 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1329 9dae41ad Guido Trotter
    except EnvironmentError, err:
1330 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1331 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1332 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1333 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1334 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1335 9dae41ad Guido Trotter
1336 9dae41ad Guido Trotter
1337 016308cb Iustin Pop
def ReadFile(file_name, size=-1):
1338 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1339 ca0aa6d0 Michael Hanselmann

1340 016308cb Iustin Pop
  @type size: int
1341 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1342 58885d79 Iustin Pop
  @rtype: str
1343 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1344 ca0aa6d0 Michael Hanselmann

1345 ca0aa6d0 Michael Hanselmann
  """
1346 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1347 ca0aa6d0 Michael Hanselmann
  try:
1348 016308cb Iustin Pop
    return f.read(size)
1349 ca0aa6d0 Michael Hanselmann
  finally:
1350 ca0aa6d0 Michael Hanselmann
    f.close()
1351 ca0aa6d0 Michael Hanselmann
1352 ca0aa6d0 Michael Hanselmann
1353 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1354 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1355 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1356 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1357 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1358 087b34fe Iustin Pop
  """(Over)write a file atomically.
1359 087b34fe Iustin Pop

1360 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1361 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1362 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1363 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1364 087b34fe Iustin Pop
  mtime/atime of the file.
1365 087b34fe Iustin Pop

1366 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1367 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1368 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1369 087b34fe Iustin Pop
  temporary file should be removed.
1370 087b34fe Iustin Pop

1371 58885d79 Iustin Pop
  @type file_name: str
1372 58885d79 Iustin Pop
  @param file_name: the target filename
1373 58885d79 Iustin Pop
  @type fn: callable
1374 58885d79 Iustin Pop
  @param fn: content writing function, called with
1375 58885d79 Iustin Pop
      file descriptor as parameter
1376 69efe319 Michael Hanselmann
  @type data: str
1377 58885d79 Iustin Pop
  @param data: contents of the file
1378 58885d79 Iustin Pop
  @type mode: int
1379 58885d79 Iustin Pop
  @param mode: file mode
1380 58885d79 Iustin Pop
  @type uid: int
1381 58885d79 Iustin Pop
  @param uid: the owner of the file
1382 58885d79 Iustin Pop
  @type gid: int
1383 58885d79 Iustin Pop
  @param gid: the group of the file
1384 58885d79 Iustin Pop
  @type atime: int
1385 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1386 58885d79 Iustin Pop
  @type mtime: int
1387 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1388 58885d79 Iustin Pop
  @type close: boolean
1389 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1390 58885d79 Iustin Pop
  @type prewrite: callable
1391 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1392 58885d79 Iustin Pop
  @type postwrite: callable
1393 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1394 58885d79 Iustin Pop

1395 58885d79 Iustin Pop
  @rtype: None or int
1396 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1397 58885d79 Iustin Pop
      otherwise the file descriptor
1398 58885d79 Iustin Pop

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

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

1455 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1456 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1457 7b4126b7 Iustin Pop
  value, the index will be returned.
1458 7b4126b7 Iustin Pop

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

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

1464 58885d79 Iustin Pop
  @type seq: sequence
1465 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1466 58885d79 Iustin Pop
  @type base: int
1467 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1468 58885d79 Iustin Pop
  @rtype: int
1469 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1470 7b4126b7 Iustin Pop

1471 7b4126b7 Iustin Pop
  """
1472 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1473 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1474 7b4126b7 Iustin Pop
    if elem > idx + base:
1475 7b4126b7 Iustin Pop
      # idx is not used
1476 7b4126b7 Iustin Pop
      return idx + base
1477 7b4126b7 Iustin Pop
  return None
1478 7b4126b7 Iustin Pop
1479 7b4126b7 Iustin Pop
1480 284c69f0 Guido Trotter
def all(seq, pred=bool): # pylint: disable-msg=W0622
1481 284c69f0 Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1482 284c69f0 Guido Trotter
  for _ in itertools.ifilterfalse(pred, seq):
1483 284c69f0 Guido Trotter
    return False
1484 284c69f0 Guido Trotter
  return True
1485 78feb6fb Guido Trotter
1486 78feb6fb Guido Trotter
1487 284c69f0 Guido Trotter
def any(seq, pred=bool): # pylint: disable-msg=W0622
1488 284c69f0 Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1489 284c69f0 Guido Trotter
  for _ in itertools.ifilter(pred, seq):
1490 284c69f0 Guido Trotter
    return True
1491 284c69f0 Guido Trotter
  return False
1492 f7414041 Michael Hanselmann
1493 f7414041 Michael Hanselmann
1494 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1495 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1496 dcd511c8 Guido Trotter

1497 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1498 dfdc4060 Guido Trotter

1499 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1500 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1501 dfdc4060 Guido Trotter
  @type event: integer
1502 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1503 dcd511c8 Guido Trotter
  @type timeout: float or None
1504 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1505 dcd511c8 Guido Trotter
  @rtype: int or None
1506 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1507 dcd511c8 Guido Trotter

1508 dcd511c8 Guido Trotter
  """
1509 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1510 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1511 dcd511c8 Guido Trotter
1512 dcd511c8 Guido Trotter
  if timeout is not None:
1513 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1514 dcd511c8 Guido Trotter
    timeout *= 1000
1515 dcd511c8 Guido Trotter
1516 dcd511c8 Guido Trotter
  poller = select.poll()
1517 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1518 dcd511c8 Guido Trotter
  try:
1519 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1520 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1521 dfdc4060 Guido Trotter
    # every so often.
1522 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1523 dfdc4060 Guido Trotter
  except select.error, err:
1524 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1525 dfdc4060 Guido Trotter
      raise
1526 dfdc4060 Guido Trotter
    io_events = []
1527 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1528 dfdc4060 Guido Trotter
    return io_events[0][1]
1529 dfdc4060 Guido Trotter
  else:
1530 dfdc4060 Guido Trotter
    return None
1531 dfdc4060 Guido Trotter
1532 dfdc4060 Guido Trotter
1533 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1534 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1535 dfdc4060 Guido Trotter

1536 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1537 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1538 dfdc4060 Guido Trotter
  expired.
1539 dfdc4060 Guido Trotter

1540 dfdc4060 Guido Trotter
  """
1541 dfdc4060 Guido Trotter
1542 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1543 dfdc4060 Guido Trotter
    self.timeout = timeout
1544 dfdc4060 Guido Trotter
1545 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1546 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1547 dfdc4060 Guido Trotter
    if result is None:
1548 dfdc4060 Guido Trotter
      raise RetryAgain()
1549 dfdc4060 Guido Trotter
    else:
1550 dfdc4060 Guido Trotter
      return result
1551 dfdc4060 Guido Trotter
1552 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1553 dfdc4060 Guido Trotter
    self.timeout = timeout
1554 dfdc4060 Guido Trotter
1555 dfdc4060 Guido Trotter
1556 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1557 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1558 dfdc4060 Guido Trotter

1559 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1560 dfdc4060 Guido Trotter

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

1570 dfdc4060 Guido Trotter
  """
1571 dfdc4060 Guido Trotter
  if timeout is not None:
1572 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1573 dfdc4060 Guido Trotter
    result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1574 dfdc4060 Guido Trotter
                   args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1575 dfdc4060 Guido Trotter
  else:
1576 dfdc4060 Guido Trotter
    result = None
1577 dfdc4060 Guido Trotter
    while result is None:
1578 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1579 dfdc4060 Guido Trotter
  return result
1580 dcd511c8 Guido Trotter
1581 dcd511c8 Guido Trotter
1582 2de64672 Iustin Pop
def partition(seq, pred=bool): # # pylint: disable-msg=W0622
1583 2de64672 Iustin Pop
  "Partition a list in two, based on the given predicate"
1584 2de64672 Iustin Pop
  return (list(itertools.ifilter(pred, seq)),
1585 2de64672 Iustin Pop
          list(itertools.ifilterfalse(pred, seq)))
1586 2de64672 Iustin Pop
1587 2de64672 Iustin Pop
1588 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1589 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1590 f7414041 Michael Hanselmann

1591 f7414041 Michael Hanselmann
  Element order is preserved.
1592 58885d79 Iustin Pop

1593 58885d79 Iustin Pop
  @type seq: sequence
1594 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1595 58885d79 Iustin Pop
  @rtype: list
1596 58885d79 Iustin Pop
  @return: list of unique elements from seq
1597 58885d79 Iustin Pop

1598 f7414041 Michael Hanselmann
  """
1599 f7414041 Michael Hanselmann
  seen = set()
1600 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1601 1862d460 Alexander Schreiber
1602 1862d460 Alexander Schreiber
1603 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
1604 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
1605 1862d460 Alexander Schreiber

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

1609 58885d79 Iustin Pop
  @type mac: str
1610 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1611 82187135 René Nussbaumer
  @rtype: str
1612 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
1613 82187135 René Nussbaumer

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

1616 1862d460 Alexander Schreiber
  """
1617 82187135 René Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1618 82187135 René Nussbaumer
  if not mac_check.match(mac):
1619 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1620 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
1621 82187135 René Nussbaumer
1622 82187135 René Nussbaumer
  return mac.lower()
1623 06009e27 Iustin Pop
1624 06009e27 Iustin Pop
1625 06009e27 Iustin Pop
def TestDelay(duration):
1626 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1627 06009e27 Iustin Pop

1628 58885d79 Iustin Pop
  @type duration: float
1629 58885d79 Iustin Pop
  @param duration: the sleep duration
1630 58885d79 Iustin Pop
  @rtype: boolean
1631 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1632 58885d79 Iustin Pop

1633 06009e27 Iustin Pop
  """
1634 06009e27 Iustin Pop
  if duration < 0:
1635 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1636 06009e27 Iustin Pop
  time.sleep(duration)
1637 38ea42a1 Iustin Pop
  return True, None
1638 8f765069 Iustin Pop
1639 8f765069 Iustin Pop
1640 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1641 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1642 8f765069 Iustin Pop

1643 7d88772a Iustin Pop
  @type fd: int
1644 7d88772a Iustin Pop
  @param fd: the file descriptor
1645 7d88772a Iustin Pop
  @type retries: int
1646 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1647 7d88772a Iustin Pop
      other error than EBADF
1648 7d88772a Iustin Pop

1649 7d88772a Iustin Pop
  """
1650 7d88772a Iustin Pop
  try:
1651 7d88772a Iustin Pop
    os.close(fd)
1652 7d88772a Iustin Pop
  except OSError, err:
1653 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1654 7d88772a Iustin Pop
      if retries > 0:
1655 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1656 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1657 7d88772a Iustin Pop
    # ignore this and go on
1658 7d88772a Iustin Pop
1659 7d88772a Iustin Pop
1660 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1661 7d88772a Iustin Pop
  """Close file descriptors.
1662 7d88772a Iustin Pop

1663 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1664 7d88772a Iustin Pop
  stdin/out/err).
1665 8f765069 Iustin Pop

1666 58885d79 Iustin Pop
  @type noclose_fds: list or None
1667 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1668 58885d79 Iustin Pop
      that should not be closed
1669 58885d79 Iustin Pop

1670 8f765069 Iustin Pop
  """
1671 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1672 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1673 8f765069 Iustin Pop
    try:
1674 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1675 8f765069 Iustin Pop
      if MAXFD < 0:
1676 8f765069 Iustin Pop
        MAXFD = 1024
1677 8f765069 Iustin Pop
    except OSError:
1678 8f765069 Iustin Pop
      MAXFD = 1024
1679 8f765069 Iustin Pop
  else:
1680 8f765069 Iustin Pop
    MAXFD = 1024
1681 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1682 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1683 7d88772a Iustin Pop
    maxfd = MAXFD
1684 7d88772a Iustin Pop
1685 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1686 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1687 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1688 7d88772a Iustin Pop
      continue
1689 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1690 7d88772a Iustin Pop
1691 7d88772a Iustin Pop
1692 7d88772a Iustin Pop
def Daemonize(logfile):
1693 7d88772a Iustin Pop
  """Daemonize the current process.
1694 7d88772a Iustin Pop

1695 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1696 7d88772a Iustin Pop
  runs it in the background as a daemon.
1697 7d88772a Iustin Pop

1698 7d88772a Iustin Pop
  @type logfile: str
1699 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1700 7d88772a Iustin Pop
  @rtype: int
1701 5fcc718f Iustin Pop
  @return: the value zero
1702 7d88772a Iustin Pop

1703 7d88772a Iustin Pop
  """
1704 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1705 7260cfbe Iustin Pop
  # yes, we really want os._exit
1706 7d88772a Iustin Pop
  UMASK = 077
1707 7d88772a Iustin Pop
  WORKDIR = "/"
1708 8f765069 Iustin Pop
1709 8f765069 Iustin Pop
  # this might fail
1710 8f765069 Iustin Pop
  pid = os.fork()
1711 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1712 8f765069 Iustin Pop
    os.setsid()
1713 8f765069 Iustin Pop
    # this might fail
1714 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1715 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1716 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1717 8f765069 Iustin Pop
      os.umask(UMASK)
1718 8f765069 Iustin Pop
    else:
1719 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1720 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1721 8f765069 Iustin Pop
  else:
1722 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1723 8f765069 Iustin Pop
1724 7d88772a Iustin Pop
  for fd in range(3):
1725 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1726 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1727 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1728 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1729 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1730 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1731 7d88772a Iustin Pop
  os.dup2(1, 2)
1732 8f765069 Iustin Pop
  return 0
1733 57c177af Iustin Pop
1734 57c177af Iustin Pop
1735 53beffbb Iustin Pop
def DaemonPidFileName(name):
1736 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1737 58885d79 Iustin Pop

1738 58885d79 Iustin Pop
  @type name: str
1739 58885d79 Iustin Pop
  @param name: the daemon name
1740 58885d79 Iustin Pop
  @rtype: str
1741 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1742 58885d79 Iustin Pop
      daemon name
1743 b330ac0b Guido Trotter

1744 b330ac0b Guido Trotter
  """
1745 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1746 b330ac0b Guido Trotter
1747 b330ac0b Guido Trotter
1748 2826b361 Guido Trotter
def EnsureDaemon(name):
1749 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1750 2826b361 Guido Trotter

1751 2826b361 Guido Trotter
  """
1752 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1753 2826b361 Guido Trotter
  if result.failed:
1754 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1755 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1756 2826b361 Guido Trotter
    return False
1757 2826b361 Guido Trotter
1758 2826b361 Guido Trotter
  return True
1759 2826b361 Guido Trotter
1760 2826b361 Guido Trotter
1761 b330ac0b Guido Trotter
def WritePidFile(name):
1762 b330ac0b Guido Trotter
  """Write the current process pidfile.
1763 b330ac0b Guido Trotter

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

1766 58885d79 Iustin Pop
  @type name: str
1767 58885d79 Iustin Pop
  @param name: the daemon name to use
1768 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1769 58885d79 Iustin Pop
      points to a live process
1770 b330ac0b Guido Trotter

1771 b330ac0b Guido Trotter
  """
1772 b330ac0b Guido Trotter
  pid = os.getpid()
1773 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1774 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1775 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1776 b330ac0b Guido Trotter
1777 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1778 b330ac0b Guido Trotter
1779 b330ac0b Guido Trotter
1780 b330ac0b Guido Trotter
def RemovePidFile(name):
1781 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1782 b330ac0b Guido Trotter

1783 b330ac0b Guido Trotter
  Any errors are ignored.
1784 b330ac0b Guido Trotter

1785 58885d79 Iustin Pop
  @type name: str
1786 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1787 58885d79 Iustin Pop

1788 b330ac0b Guido Trotter
  """
1789 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1790 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1791 b330ac0b Guido Trotter
  try:
1792 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1793 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1794 b330ac0b Guido Trotter
    pass
1795 b330ac0b Guido Trotter
1796 b330ac0b Guido Trotter
1797 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1798 ff5251bc Iustin Pop
                waitpid=False):
1799 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1800 b2a1f511 Iustin Pop

1801 b2a1f511 Iustin Pop
  @type pid: int
1802 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1803 38206f3c Iustin Pop
  @type signal_: int
1804 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1805 b2a1f511 Iustin Pop
  @type timeout: int
1806 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1807 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1808 b2a1f511 Iustin Pop
                  will be done
1809 ff5251bc Iustin Pop
  @type waitpid: boolean
1810 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1811 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1812 ff5251bc Iustin Pop
      would remain as zombie
1813 b2a1f511 Iustin Pop

1814 b2a1f511 Iustin Pop
  """
1815 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1816 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1817 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1818 ff5251bc Iustin Pop
    if wait:
1819 ff5251bc Iustin Pop
      try:
1820 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1821 ff5251bc Iustin Pop
      except OSError:
1822 ff5251bc Iustin Pop
        pass
1823 ff5251bc Iustin Pop
1824 b2a1f511 Iustin Pop
  if pid <= 0:
1825 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1826 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1827 b2a1f511 Iustin Pop
1828 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1829 b2a1f511 Iustin Pop
    return
1830 31892b4c Michael Hanselmann
1831 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1832 31892b4c Michael Hanselmann
1833 b2a1f511 Iustin Pop
  if timeout <= 0:
1834 b2a1f511 Iustin Pop
    return
1835 7167159a Michael Hanselmann
1836 31892b4c Michael Hanselmann
  def _CheckProcess():
1837 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1838 31892b4c Michael Hanselmann
      return
1839 31892b4c Michael Hanselmann
1840 7167159a Michael Hanselmann
    try:
1841 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1842 7167159a Michael Hanselmann
    except OSError:
1843 31892b4c Michael Hanselmann
      raise RetryAgain()
1844 31892b4c Michael Hanselmann
1845 31892b4c Michael Hanselmann
    if result_pid > 0:
1846 31892b4c Michael Hanselmann
      return
1847 31892b4c Michael Hanselmann
1848 31892b4c Michael Hanselmann
    raise RetryAgain()
1849 31892b4c Michael Hanselmann
1850 31892b4c Michael Hanselmann
  try:
1851 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1852 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1853 31892b4c Michael Hanselmann
  except RetryTimeout:
1854 31892b4c Michael Hanselmann
    pass
1855 7167159a Michael Hanselmann
1856 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1857 7167159a Michael Hanselmann
    # Kill process if it's still alive
1858 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1859 b2a1f511 Iustin Pop
1860 b2a1f511 Iustin Pop
1861 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1862 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1863 57c177af Iustin Pop

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

1867 58885d79 Iustin Pop
  @type name: str
1868 58885d79 Iustin Pop
  @param name: the name to look for
1869 58885d79 Iustin Pop
  @type search_path: str
1870 58885d79 Iustin Pop
  @param search_path: location to start at
1871 58885d79 Iustin Pop
  @type test: callable
1872 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1873 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1874 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1875 58885d79 Iustin Pop
  @rtype: str or None
1876 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1877 57c177af Iustin Pop

1878 57c177af Iustin Pop
  """
1879 f95c81bf Iustin Pop
  # validate the filename mask
1880 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1881 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1882 f95c81bf Iustin Pop
                     name)
1883 f95c81bf Iustin Pop
    return None
1884 f95c81bf Iustin Pop
1885 57c177af Iustin Pop
  for dir_name in search_path:
1886 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1887 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1888 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1889 f95c81bf Iustin Pop
    # basename
1890 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1891 57c177af Iustin Pop
      return item_name
1892 57c177af Iustin Pop
  return None
1893 8d1a2a64 Michael Hanselmann
1894 8d1a2a64 Michael Hanselmann
1895 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1896 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1897 8d1a2a64 Michael Hanselmann

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

1901 58885d79 Iustin Pop
  @type vglist: dict
1902 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1903 58885d79 Iustin Pop
  @type vgname: str
1904 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1905 58885d79 Iustin Pop
  @type minsize: int
1906 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1907 58885d79 Iustin Pop
  @rtype: None or str
1908 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1909 8d1a2a64 Michael Hanselmann

1910 8d1a2a64 Michael Hanselmann
  """
1911 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1912 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1913 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1914 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1915 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1916 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1917 8d1a2a64 Michael Hanselmann
  return None
1918 7996a135 Iustin Pop
1919 7996a135 Iustin Pop
1920 45bc5e4a Michael Hanselmann
def SplitTime(value):
1921 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1922 739be818 Michael Hanselmann

1923 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1924 45bc5e4a Michael Hanselmann
  @type value: int or float
1925 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1926 739be818 Michael Hanselmann

1927 739be818 Michael Hanselmann
  """
1928 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1929 45bc5e4a Michael Hanselmann
1930 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1931 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1932 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1933 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1934 45bc5e4a Michael Hanselmann
1935 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1936 739be818 Michael Hanselmann
1937 739be818 Michael Hanselmann
1938 739be818 Michael Hanselmann
def MergeTime(timetuple):
1939 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1940 739be818 Michael Hanselmann

1941 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1942 739be818 Michael Hanselmann
  @type timetuple: tuple
1943 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1944 739be818 Michael Hanselmann

1945 739be818 Michael Hanselmann
  """
1946 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1947 739be818 Michael Hanselmann
1948 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1949 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1950 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1951 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1952 739be818 Michael Hanselmann
1953 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1954 739be818 Michael Hanselmann
1955 739be818 Michael Hanselmann
1956 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1957 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1958 4a8b186a Michael Hanselmann

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

1963 cd50653c Guido Trotter
  @type daemon_name: string
1964 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1965 58885d79 Iustin Pop
  @rtype: int
1966 58885d79 Iustin Pop

1967 4a8b186a Michael Hanselmann
  """
1968 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1969 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1970 cd50653c Guido Trotter
1971 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1972 4a8b186a Michael Hanselmann
  try:
1973 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1974 4a8b186a Michael Hanselmann
  except socket.error:
1975 cd50653c Guido Trotter
    port = default_port
1976 4a8b186a Michael Hanselmann
1977 4a8b186a Michael Hanselmann
  return port
1978 4a8b186a Michael Hanselmann
1979 4a8b186a Michael Hanselmann
1980 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
1981 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
1982 82d9caef Iustin Pop
  """Configures the logging module.
1983 82d9caef Iustin Pop

1984 58885d79 Iustin Pop
  @type logfile: str
1985 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1986 ea34193f Iustin Pop
  @type debug: integer
1987 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
1988 58885d79 Iustin Pop
      only those at C{INFO} and above level
1989 58885d79 Iustin Pop
  @type stderr_logging: boolean
1990 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1991 58885d79 Iustin Pop
  @type program: str
1992 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1993 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1994 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1995 551b6283 Iustin Pop
  @type syslog: string
1996 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
1997 551b6283 Iustin Pop
      - if no, syslog is not used
1998 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
1999 551b6283 Iustin Pop
      - if only, only syslog is used
2000 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2001 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2002 58885d79 Iustin Pop

2003 82d9caef Iustin Pop
  """
2004 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2005 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2006 d21d09d6 Iustin Pop
  if multithreaded:
2007 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2008 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2009 82d9caef Iustin Pop
  if debug:
2010 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2011 551b6283 Iustin Pop
    # no debug info for syslog loggers
2012 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2013 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2014 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2015 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2016 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2017 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2018 82d9caef Iustin Pop
2019 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2020 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2021 82d9caef Iustin Pop
2022 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2023 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2024 7d88772a Iustin Pop
    handler.close()
2025 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2026 6346a9e5 Michael Hanselmann
2027 82d9caef Iustin Pop
  if stderr_logging:
2028 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2029 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2030 82d9caef Iustin Pop
    if debug:
2031 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2032 82d9caef Iustin Pop
    else:
2033 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2034 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2035 82d9caef Iustin Pop
2036 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2037 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2038 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2039 551b6283 Iustin Pop
                                                    facility)
2040 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2041 551b6283 Iustin Pop
    # Never enable debug over syslog
2042 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2043 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2044 551b6283 Iustin Pop
2045 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2046 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2047 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2048 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2049 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2050 551b6283 Iustin Pop
    try:
2051 551b6283 Iustin Pop
      logfile_handler = logging.FileHandler(logfile)
2052 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2053 551b6283 Iustin Pop
      if debug:
2054 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2055 551b6283 Iustin Pop
      else:
2056 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2057 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2058 551b6283 Iustin Pop
    except EnvironmentError:
2059 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2060 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2061 551b6283 Iustin Pop
      else:
2062 551b6283 Iustin Pop
        # we need to re-raise the exception
2063 551b6283 Iustin Pop
        raise
2064 82d9caef Iustin Pop
2065 016d04b3 Michael Hanselmann
2066 da961187 Guido Trotter
def IsNormAbsPath(path):
2067 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2068 da961187 Guido Trotter

2069 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2070 da961187 Guido Trotter

2071 da961187 Guido Trotter
  """
2072 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2073 82d9caef Iustin Pop
2074 016d04b3 Michael Hanselmann
2075 4bb678e9 Iustin Pop
def PathJoin(*args):
2076 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2077 4bb678e9 Iustin Pop

2078 4bb678e9 Iustin Pop
  Requirements:
2079 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2080 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2081 4bb678e9 Iustin Pop
        since we check for normalization at the end
2082 4bb678e9 Iustin Pop

2083 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2084 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2085 4bb678e9 Iustin Pop

2086 4bb678e9 Iustin Pop
  """
2087 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2088 4bb678e9 Iustin Pop
  assert args
2089 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2090 4bb678e9 Iustin Pop
  root = args[0]
2091 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2092 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2093 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2094 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2095 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2096 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2097 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2098 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2099 4bb678e9 Iustin Pop
  if prefix != root:
2100 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2101 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2102 4bb678e9 Iustin Pop
  return result
2103 4bb678e9 Iustin Pop
2104 4bb678e9 Iustin Pop
2105 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2106 f65f63ef Iustin Pop
  """Return the last lines from a file.
2107 f65f63ef Iustin Pop

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

2112 f65f63ef Iustin Pop
  @param fname: the file name
2113 f65f63ef Iustin Pop
  @type lines: int
2114 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2115 f65f63ef Iustin Pop

2116 f65f63ef Iustin Pop
  """
2117 f65f63ef Iustin Pop
  fd = open(fname, "r")
2118 f65f63ef Iustin Pop
  try:
2119 f65f63ef Iustin Pop
    fd.seek(0, 2)
2120 f65f63ef Iustin Pop
    pos = fd.tell()
2121 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2122 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2123 f65f63ef Iustin Pop
    raw_data = fd.read()
2124 f65f63ef Iustin Pop
  finally:
2125 f65f63ef Iustin Pop
    fd.close()
2126 f65f63ef Iustin Pop
2127 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2128 f65f63ef Iustin Pop
  return rows[-lines:]
2129 f65f63ef Iustin Pop
2130 f65f63ef Iustin Pop
2131 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2132 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2133 27e46076 Michael Hanselmann

2134 27e46076 Michael Hanselmann
  @type value: string
2135 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2136 27e46076 Michael Hanselmann

2137 27e46076 Michael Hanselmann
  """
2138 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2139 27e46076 Michael Hanselmann
  if m:
2140 27e46076 Michael Hanselmann
    # We have an offset
2141 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2142 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2143 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2144 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2145 27e46076 Michael Hanselmann
  else:
2146 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2147 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2148 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2149 27e46076 Michael Hanselmann
    utcoffset = 0
2150 27e46076 Michael Hanselmann
2151 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2152 27e46076 Michael Hanselmann
2153 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2154 27e46076 Michael Hanselmann
2155 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2156 27e46076 Michael Hanselmann
2157 27e46076 Michael Hanselmann
2158 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2159 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2160 27e46076 Michael Hanselmann

2161 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2162 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2163 27e46076 Michael Hanselmann

2164 27e46076 Michael Hanselmann
  """
2165 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2166 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2167 27e46076 Michael Hanselmann
  try:
2168 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2169 27e46076 Michael Hanselmann
  except AttributeError:
2170 27e46076 Michael Hanselmann
    not_before = None
2171 27e46076 Michael Hanselmann
  else:
2172 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2173 27e46076 Michael Hanselmann
2174 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2175 27e46076 Michael Hanselmann
      not_before = None
2176 27e46076 Michael Hanselmann
    else:
2177 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2178 27e46076 Michael Hanselmann
2179 27e46076 Michael Hanselmann
  try:
2180 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2181 27e46076 Michael Hanselmann
  except AttributeError:
2182 27e46076 Michael Hanselmann
    not_after = None
2183 27e46076 Michael Hanselmann
  else:
2184 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2185 27e46076 Michael Hanselmann
2186 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2187 27e46076 Michael Hanselmann
      not_after = None
2188 27e46076 Michael Hanselmann
    else:
2189 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2190 27e46076 Michael Hanselmann
2191 27e46076 Michael Hanselmann
  return (not_before, not_after)
2192 27e46076 Michael Hanselmann
2193 27e46076 Michael Hanselmann
2194 26f15862 Iustin Pop
def SafeEncode(text):
2195 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2196 26f15862 Iustin Pop

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

2206 26f15862 Iustin Pop
  @type text: str or unicode
2207 26f15862 Iustin Pop
  @param text: input data
2208 26f15862 Iustin Pop
  @rtype: str
2209 26f15862 Iustin Pop
  @return: a safe version of text
2210 26f15862 Iustin Pop

2211 26f15862 Iustin Pop
  """
2212 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2213 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2214 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2215 d392fa34 Iustin Pop
  resu = ""
2216 d392fa34 Iustin Pop
  for char in text:
2217 d392fa34 Iustin Pop
    c = ord(char)
2218 d392fa34 Iustin Pop
    if char  == '\t':
2219 d392fa34 Iustin Pop
      resu += r'\t'
2220 d392fa34 Iustin Pop
    elif char == '\n':
2221 d392fa34 Iustin Pop
      resu += r'\n'
2222 d392fa34 Iustin Pop
    elif char == '\r':
2223 d392fa34 Iustin Pop
      resu += r'\'r'
2224 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2225 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2226 d392fa34 Iustin Pop
    else:
2227 d392fa34 Iustin Pop
      resu += char
2228 d392fa34 Iustin Pop
  return resu
2229 26f15862 Iustin Pop
2230 26f15862 Iustin Pop
2231 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2232 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2233 5b69bc7c Iustin Pop

2234 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2235 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2236 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2237 5b69bc7c Iustin Pop
  separator):
2238 5b69bc7c Iustin Pop
    - a plain , separates the elements
2239 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2240 5b69bc7c Iustin Pop
      backslash plus a separator comma
2241 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2242 5b69bc7c Iustin Pop
      non-separator comma
2243 5b69bc7c Iustin Pop

2244 5b69bc7c Iustin Pop
  @type text: string
2245 5b69bc7c Iustin Pop
  @param text: the string to split
2246 5b69bc7c Iustin Pop
  @type sep: string
2247 5b69bc7c Iustin Pop
  @param text: the separator
2248 5b69bc7c Iustin Pop
  @rtype: string
2249 5b69bc7c Iustin Pop
  @return: a list of strings
2250 5b69bc7c Iustin Pop

2251 5b69bc7c Iustin Pop
  """
2252 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2253 5b69bc7c Iustin Pop
  slist = text.split(sep)
2254 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2255 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2256 5b69bc7c Iustin Pop
  rlist = []
2257 5b69bc7c Iustin Pop
  while slist:
2258 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2259 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2260 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2261 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2262 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2263 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2264 5b69bc7c Iustin Pop
        # the next step
2265 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2266 5b69bc7c Iustin Pop
        continue
2267 5b69bc7c Iustin Pop
    rlist.append(e1)
2268 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2269 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2270 5b69bc7c Iustin Pop
  return rlist
2271 5b69bc7c Iustin Pop
2272 5b69bc7c Iustin Pop
2273 ab3e6da8 Iustin Pop
def CommaJoin(names):
2274 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2275 ab3e6da8 Iustin Pop

2276 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2277 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2278 ab3e6da8 Iustin Pop

2279 ab3e6da8 Iustin Pop
  """
2280 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2281 ab3e6da8 Iustin Pop
2282 ab3e6da8 Iustin Pop
2283 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2284 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2285 3f6a47a8 Michael Hanselmann

2286 3f6a47a8 Michael Hanselmann
  @type value: int
2287 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2288 3f6a47a8 Michael Hanselmann
  @rtype: int
2289 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2290 3f6a47a8 Michael Hanselmann

2291 3f6a47a8 Michael Hanselmann
  """
2292 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2293 3f6a47a8 Michael Hanselmann
2294 3f6a47a8 Michael Hanselmann
2295 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2296 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2297 3f6a47a8 Michael Hanselmann

2298 3f6a47a8 Michael Hanselmann
  @type path: string
2299 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2300 3f6a47a8 Michael Hanselmann
  @rtype: int
2301 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2302 3f6a47a8 Michael Hanselmann

2303 3f6a47a8 Michael Hanselmann
  """
2304 3f6a47a8 Michael Hanselmann
  size = 0
2305 3f6a47a8 Michael Hanselmann
2306 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2307 2a887df9 Michael Hanselmann
    for filename in files:
2308 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2309 3f6a47a8 Michael Hanselmann
      size += st.st_size
2310 3f6a47a8 Michael Hanselmann
2311 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2312 3f6a47a8 Michael Hanselmann
2313 3f6a47a8 Michael Hanselmann
2314 620a85fd Iustin Pop
def GetFilesystemStats(path):
2315 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2316 3f6a47a8 Michael Hanselmann

2317 3f6a47a8 Michael Hanselmann
  @type path: string
2318 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2319 3f6a47a8 Michael Hanselmann
  @rtype: int
2320 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2321 3f6a47a8 Michael Hanselmann

2322 3f6a47a8 Michael Hanselmann
  """
2323 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2324 3f6a47a8 Michael Hanselmann
2325 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2326 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2327 620a85fd Iustin Pop
  return (tsize, fsize)
2328 3f6a47a8 Michael Hanselmann
2329 3f6a47a8 Michael Hanselmann
2330 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2331 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2332 eb58f7bd Michael Hanselmann

2333 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2334 eb58f7bd Michael Hanselmann

2335 eb58f7bd Michael Hanselmann
  @type fn: callable
2336 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2337 bdefe5dd Michael Hanselmann
  @rtype: bool
2338 bdefe5dd Michael Hanselmann
  @return: Function's result
2339 eb58f7bd Michael Hanselmann

2340 eb58f7bd Michael Hanselmann
  """
2341 eb58f7bd Michael Hanselmann
  pid = os.fork()
2342 eb58f7bd Michael Hanselmann
  if pid == 0:
2343 eb58f7bd Michael Hanselmann
    # Child process
2344 eb58f7bd Michael Hanselmann
    try:
2345 82869978 Michael Hanselmann
      # In case the function uses temporary files
2346 82869978 Michael Hanselmann
      ResetTempfileModule()
2347 82869978 Michael Hanselmann
2348 eb58f7bd Michael Hanselmann
      # Call function
2349 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2350 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2351 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2352 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2353 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2354 eb58f7bd Michael Hanselmann
      result = 33
2355 eb58f7bd Michael Hanselmann
2356 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2357 eb58f7bd Michael Hanselmann
2358 eb58f7bd Michael Hanselmann
  # Parent process
2359 eb58f7bd Michael Hanselmann
2360 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2361 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2362 eb58f7bd Michael Hanselmann
2363 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2364 eb58f7bd Michael Hanselmann
    exitcode = None
2365 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2366 eb58f7bd Michael Hanselmann
  else:
2367 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2368 eb58f7bd Michael Hanselmann
    signum = None
2369 eb58f7bd Michael Hanselmann
2370 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2371 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2372 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2373 eb58f7bd Michael Hanselmann
2374 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2375 eb58f7bd Michael Hanselmann
2376 eb58f7bd Michael Hanselmann
2377 7996a135 Iustin Pop
def LockedMethod(fn):
2378 7996a135 Iustin Pop
  """Synchronized object access decorator.
2379 7996a135 Iustin Pop

2380 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2381 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2382 7996a135 Iustin Pop

2383 7996a135 Iustin Pop
  """
2384 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2385 e67bd559 Michael Hanselmann
    if debug_locks:
2386 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2387 e67bd559 Michael Hanselmann
2388 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2389 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2390 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2391 7996a135 Iustin Pop
    lock = self._lock
2392 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2393 7996a135 Iustin Pop
    lock.acquire()
2394 7996a135 Iustin Pop
    try:
2395 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2396 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2397 7996a135 Iustin Pop
    finally:
2398 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2399 7996a135 Iustin Pop
      lock.release()
2400 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2401 7996a135 Iustin Pop
    return result
2402 7996a135 Iustin Pop
  return wrapper
2403 eb0f0ce0 Michael Hanselmann
2404 eb0f0ce0 Michael Hanselmann
2405 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2406 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2407 eb0f0ce0 Michael Hanselmann

2408 58885d79 Iustin Pop
  @type fd: int
2409 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2410 58885d79 Iustin Pop

2411 eb0f0ce0 Michael Hanselmann
  """
2412 eb0f0ce0 Michael Hanselmann
  try:
2413 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2414 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2415 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2416 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2417 eb0f0ce0 Michael Hanselmann
    raise
2418 de499029 Michael Hanselmann
2419 de499029 Michael Hanselmann
2420 3b813dd2 Iustin Pop
def FormatTime(val):
2421 3b813dd2 Iustin Pop
  """Formats a time value.
2422 3b813dd2 Iustin Pop

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

2427 3b813dd2 Iustin Pop
  """
2428 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2429 3b813dd2 Iustin Pop
    return "N/A"
2430 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2431 3b813dd2 Iustin Pop
  # platforms
2432 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2433 3b813dd2 Iustin Pop
2434 3b813dd2 Iustin Pop
2435 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2436 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2437 05e50653 Michael Hanselmann

2438 5cbe43a5 Michael Hanselmann
  @type filename: string
2439 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2440 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2441 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2442 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2443 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2444 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2445 5cbe43a5 Michael Hanselmann

2446 05e50653 Michael Hanselmann
  """
2447 05e50653 Michael Hanselmann
  if now is None:
2448 05e50653 Michael Hanselmann
    now = time.time()
2449 05e50653 Michael Hanselmann
2450 05e50653 Michael Hanselmann
  try:
2451 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2452 05e50653 Michael Hanselmann
  except IOError, err:
2453 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2454 05e50653 Michael Hanselmann
      raise
2455 05e50653 Michael Hanselmann
    value = None
2456 05e50653 Michael Hanselmann
2457 05e50653 Michael Hanselmann
  if value is not None:
2458 05e50653 Michael Hanselmann
    try:
2459 05e50653 Michael Hanselmann
      value = int(value)
2460 05e50653 Michael Hanselmann
    except ValueError:
2461 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2462 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2463 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2464 05e50653 Michael Hanselmann
      value = None
2465 05e50653 Michael Hanselmann
2466 05e50653 Michael Hanselmann
    if value is not None:
2467 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2468 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2469 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2470 5cbe43a5 Michael Hanselmann
        value = None
2471 5cbe43a5 Michael Hanselmann
2472 5cbe43a5 Michael Hanselmann
      elif now > value:
2473 05e50653 Michael Hanselmann
        value = None
2474 05e50653 Michael Hanselmann
2475 05e50653 Michael Hanselmann
  return value
2476 05e50653 Michael Hanselmann
2477 05e50653 Michael Hanselmann
2478 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2479 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2480 de0ea66b Michael Hanselmann

2481 de0ea66b Michael Hanselmann
  """
2482 de0ea66b Michael Hanselmann
2483 de0ea66b Michael Hanselmann
2484 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2485 de0ea66b Michael Hanselmann
  """Retry again.
2486 de0ea66b Michael Hanselmann

2487 de0ea66b Michael Hanselmann
  """
2488 de0ea66b Michael Hanselmann
2489 de0ea66b Michael Hanselmann
2490 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2491 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2492 de0ea66b Michael Hanselmann

2493 de0ea66b Michael Hanselmann
  """
2494 de0ea66b Michael Hanselmann
  __slots__ = [
2495 de0ea66b Michael Hanselmann
    "_factor",
2496 de0ea66b Michael Hanselmann
    "_limit",
2497 de0ea66b Michael Hanselmann
    "_next",
2498 de0ea66b Michael Hanselmann
    "_start",
2499 de0ea66b Michael Hanselmann
    ]
2500 de0ea66b Michael Hanselmann
2501 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2502 de0ea66b Michael Hanselmann
    """Initializes this class.
2503 de0ea66b Michael Hanselmann

2504 de0ea66b Michael Hanselmann
    @type start: float
2505 de0ea66b Michael Hanselmann
    @param start: Initial delay
2506 de0ea66b Michael Hanselmann
    @type factor: float
2507 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2508 de0ea66b Michael Hanselmann
    @type limit: float or None
2509 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2510 de0ea66b Michael Hanselmann

2511 de0ea66b Michael Hanselmann
    """
2512 de0ea66b Michael Hanselmann
    assert start > 0.0
2513 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2514 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2515 de0ea66b Michael Hanselmann
2516 de0ea66b Michael Hanselmann
    self._start = start
2517 de0ea66b Michael Hanselmann
    self._factor = factor
2518 de0ea66b Michael Hanselmann
    self._limit = limit
2519 de0ea66b Michael Hanselmann
2520 de0ea66b Michael Hanselmann
    self._next = start
2521 de0ea66b Michael Hanselmann
2522 de0ea66b Michael Hanselmann
  def __call__(self):
2523 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2524 de0ea66b Michael Hanselmann

2525 de0ea66b Michael Hanselmann
    """
2526 de0ea66b Michael Hanselmann
    current = self._next
2527 de0ea66b Michael Hanselmann
2528 de0ea66b Michael Hanselmann
    # Update for next run
2529 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2530 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2531 de0ea66b Michael Hanselmann
2532 de0ea66b Michael Hanselmann
    return current
2533 de0ea66b Michael Hanselmann
2534 de0ea66b Michael Hanselmann
2535 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2536 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2537 de0ea66b Michael Hanselmann
2538 de0ea66b Michael Hanselmann
2539 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2540 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2541 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2542 de0ea66b Michael Hanselmann

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

2547 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2548 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2549 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2550 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2551 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2552 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2553 de0ea66b Michael Hanselmann

2554 de0ea66b Michael Hanselmann
  @type fn: callable
2555 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2556 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2557 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2558 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2559 de0ea66b Michael Hanselmann
  @type timeout: float
2560 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2561 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2562 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2563 de0ea66b Michael Hanselmann
  @return: Return value of function
2564 de0ea66b Michael Hanselmann

2565 de0ea66b Michael Hanselmann
  """
2566 de0ea66b Michael Hanselmann
  assert callable(fn)
2567 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2568 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2569 de0ea66b Michael Hanselmann
2570 de0ea66b Michael Hanselmann
  if args is None:
2571 de0ea66b Michael Hanselmann
    args = []
2572 de0ea66b Michael Hanselmann
2573 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2574 de0ea66b Michael Hanselmann
2575 de0ea66b Michael Hanselmann
  if callable(delay):
2576 de0ea66b Michael Hanselmann
    # External function to calculate delay
2577 de0ea66b Michael Hanselmann
    calc_delay = delay
2578 de0ea66b Michael Hanselmann
2579 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2580 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2581 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2582 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2583 de0ea66b Michael Hanselmann
2584 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2585 de0ea66b Michael Hanselmann
    # Always use the remaining time
2586 de0ea66b Michael Hanselmann
    calc_delay = None
2587 de0ea66b Michael Hanselmann
2588 de0ea66b Michael Hanselmann
  else:
2589 de0ea66b Michael Hanselmann
    # Static delay
2590 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2591 de0ea66b Michael Hanselmann
2592 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2593 de0ea66b Michael Hanselmann
2594 de0ea66b Michael Hanselmann
  while True:
2595 de0ea66b Michael Hanselmann
    try:
2596 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2597 de0ea66b Michael Hanselmann
      return fn(*args)
2598 de0ea66b Michael Hanselmann
    except RetryAgain:
2599 de0ea66b Michael Hanselmann
      pass
2600 de0ea66b Michael Hanselmann
2601 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2602 de0ea66b Michael Hanselmann
2603 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2604 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2605 de0ea66b Michael Hanselmann
2606 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2607 de0ea66b Michael Hanselmann
2608 de0ea66b Michael Hanselmann
    if calc_delay is None:
2609 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2610 de0ea66b Michael Hanselmann
    else:
2611 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2612 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2613 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2614 de0ea66b Michael Hanselmann
2615 de0ea66b Michael Hanselmann
2616 a87b4824 Michael Hanselmann
class FileLock(object):
2617 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2618 a87b4824 Michael Hanselmann

2619 a87b4824 Michael Hanselmann
  """
2620 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2621 58885d79 Iustin Pop
    """Constructor for FileLock.
2622 58885d79 Iustin Pop

2623 b4478d34 Michael Hanselmann
    @type fd: file
2624 b4478d34 Michael Hanselmann
    @param fd: File object
2625 58885d79 Iustin Pop
    @type filename: str
2626 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2627 58885d79 Iustin Pop

2628 58885d79 Iustin Pop
    """
2629 b4478d34 Michael Hanselmann
    self.fd = fd
2630 a87b4824 Michael Hanselmann
    self.filename = filename
2631 b4478d34 Michael Hanselmann
2632 b4478d34 Michael Hanselmann
  @classmethod
2633 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2634 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2635 b4478d34 Michael Hanselmann

2636 b4478d34 Michael Hanselmann
    @type filename: string
2637 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2638 b4478d34 Michael Hanselmann

2639 b4478d34 Michael Hanselmann
    """
2640 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2641 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2642 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2643 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2644 b4478d34 Michael Hanselmann
               filename)
2645 a87b4824 Michael Hanselmann
2646 a87b4824 Michael Hanselmann
  def __del__(self):
2647 a87b4824 Michael Hanselmann
    self.Close()
2648 a87b4824 Michael Hanselmann
2649 a87b4824 Michael Hanselmann
  def Close(self):
2650 58885d79 Iustin Pop
    """Close the file and release the lock.
2651 58885d79 Iustin Pop

2652 58885d79 Iustin Pop
    """
2653 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2654 a87b4824 Michael Hanselmann
      self.fd.close()
2655 a87b4824 Michael Hanselmann
      self.fd = None
2656 a87b4824 Michael Hanselmann
2657 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2658 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2659 aa74b828 Michael Hanselmann

2660 aa74b828 Michael Hanselmann
    @type flag: int
2661 58885d79 Iustin Pop
    @param flag: operation flag
2662 aa74b828 Michael Hanselmann
    @type blocking: bool
2663 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2664 aa74b828 Michael Hanselmann
    @type timeout: None or float
2665 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2666 aa74b828 Michael Hanselmann
                    non-blocking mode).
2667 aa74b828 Michael Hanselmann
    @type errmsg: string
2668 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2669 aa74b828 Michael Hanselmann

2670 aa74b828 Michael Hanselmann
    """
2671 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2672 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2673 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2674 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2675 a87b4824 Michael Hanselmann
2676 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2677 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2678 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2679 a87b4824 Michael Hanselmann
2680 cc4c9b91 Michael Hanselmann
    if timeout is None:
2681 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2682 cc4c9b91 Michael Hanselmann
    else:
2683 cc4c9b91 Michael Hanselmann
      try:
2684 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2685 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2686 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2687 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2688 aa74b828 Michael Hanselmann
2689 cc4c9b91 Michael Hanselmann
  @staticmethod
2690 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2691 cc4c9b91 Michael Hanselmann
    try:
2692 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2693 cc4c9b91 Michael Hanselmann
    except IOError, err:
2694 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2695 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2696 31892b4c Michael Hanselmann
2697 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2698 cc4c9b91 Michael Hanselmann
      raise
2699 aa74b828 Michael Hanselmann
2700 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2701 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2702 a87b4824 Michael Hanselmann

2703 58885d79 Iustin Pop
    @type blocking: boolean
2704 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2705 58885d79 Iustin Pop
        can lock the file or return immediately
2706 58885d79 Iustin Pop
    @type timeout: int or None
2707 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2708 58885d79 Iustin Pop
        (in blocking mode)
2709 58885d79 Iustin Pop

2710 a87b4824 Michael Hanselmann
    """
2711 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2712 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2713 a87b4824 Michael Hanselmann
2714 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2715 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2716 a87b4824 Michael Hanselmann

2717 58885d79 Iustin Pop
    @type blocking: boolean
2718 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2719 58885d79 Iustin Pop
        can lock the file or return immediately
2720 58885d79 Iustin Pop
    @type timeout: int or None
2721 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2722 58885d79 Iustin Pop
        (in blocking mode)
2723 58885d79 Iustin Pop

2724 a87b4824 Michael Hanselmann
    """
2725 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2726 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2727 a87b4824 Michael Hanselmann
2728 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2729 a87b4824 Michael Hanselmann
    """Unlocks the file.
2730 a87b4824 Michael Hanselmann

2731 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2732 58885d79 Iustin Pop
    operation::
2733 58885d79 Iustin Pop

2734 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2735 58885d79 Iustin Pop
      operations.
2736 58885d79 Iustin Pop

2737 58885d79 Iustin Pop
    @type blocking: boolean
2738 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2739 58885d79 Iustin Pop
        can lock the file or return immediately
2740 58885d79 Iustin Pop
    @type timeout: int or None
2741 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2742 58885d79 Iustin Pop
        (in blocking mode)
2743 a87b4824 Michael Hanselmann

2744 a87b4824 Michael Hanselmann
    """
2745 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2746 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2747 a87b4824 Michael Hanselmann
2748 a87b4824 Michael Hanselmann
2749 451575de Guido Trotter
def SignalHandled(signums):
2750 451575de Guido Trotter
  """Signal Handled decoration.
2751 451575de Guido Trotter

2752 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2753 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2754 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2755 451575de Guido Trotter
  objects as values.
2756 451575de Guido Trotter

2757 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2758 451575de Guido Trotter
  with different handlers.
2759 451575de Guido Trotter

2760 451575de Guido Trotter
  @type signums: list
2761 451575de Guido Trotter
  @param signums: signals to intercept
2762 451575de Guido Trotter

2763 451575de Guido Trotter
  """
2764 451575de Guido Trotter
  def wrap(fn):
2765 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2766 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2767 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2768 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2769 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2770 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2771 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2772 451575de Guido Trotter
      else:
2773 451575de Guido Trotter
        signal_handlers = {}
2774 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2775 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2776 451575de Guido Trotter
      try:
2777 451575de Guido Trotter
        for sig in signums:
2778 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2779 451575de Guido Trotter
        return fn(*args, **kwargs)
2780 451575de Guido Trotter
      finally:
2781 451575de Guido Trotter
        sighandler.Reset()
2782 451575de Guido Trotter
    return sig_function
2783 451575de Guido Trotter
  return wrap
2784 451575de Guido Trotter
2785 451575de Guido Trotter
2786 de499029 Michael Hanselmann
class SignalHandler(object):
2787 de499029 Michael Hanselmann
  """Generic signal handler class.
2788 de499029 Michael Hanselmann

2789 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2790 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2791 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2792 58885d79 Iustin Pop
  signal was sent.
2793 58885d79 Iustin Pop

2794 58885d79 Iustin Pop
  @type signum: list
2795 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2796 58885d79 Iustin Pop
  @type called: boolean
2797 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2798 de499029 Michael Hanselmann

2799 de499029 Michael Hanselmann
  """
2800 de499029 Michael Hanselmann
  def __init__(self, signum):
2801 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2802 de499029 Michael Hanselmann

2803 58885d79 Iustin Pop
    @type signum: int or list of ints
2804 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2805 de499029 Michael Hanselmann

2806 de499029 Michael Hanselmann
    """
2807 6c52849e Guido Trotter
    self.signum = set(signum)
2808 de499029 Michael Hanselmann
    self.called = False
2809 de499029 Michael Hanselmann
2810 de499029 Michael Hanselmann
    self._previous = {}
2811 de499029 Michael Hanselmann
    try:
2812 de499029 Michael Hanselmann
      for signum in self.signum:
2813 de499029 Michael Hanselmann
        # Setup handler
2814 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2815 de499029 Michael Hanselmann
        try:
2816 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2817 de499029 Michael Hanselmann
        except:
2818 de499029 Michael Hanselmann
          # Restore previous handler
2819 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2820 de499029 Michael Hanselmann
          raise
2821 de499029 Michael Hanselmann
    except:
2822 de499029 Michael Hanselmann
      # Reset all handlers
2823 de499029 Michael Hanselmann
      self.Reset()
2824 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2825 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2826 de499029 Michael Hanselmann
      raise
2827 de499029 Michael Hanselmann
2828 de499029 Michael Hanselmann
  def __del__(self):
2829 de499029 Michael Hanselmann
    self.Reset()
2830 de499029 Michael Hanselmann
2831 de499029 Michael Hanselmann
  def Reset(self):
2832 de499029 Michael Hanselmann
    """Restore previous handler.
2833 de499029 Michael Hanselmann

2834 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2835 58885d79 Iustin Pop

2836 de499029 Michael Hanselmann
    """
2837 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2838 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2839 de499029 Michael Hanselmann
      # If successful, remove from dict
2840 de499029 Michael Hanselmann
      del self._previous[signum]
2841 de499029 Michael Hanselmann
2842 de499029 Michael Hanselmann
  def Clear(self):
2843 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2844 de499029 Michael Hanselmann

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

2847 de499029 Michael Hanselmann
    """
2848 de499029 Michael Hanselmann
    self.called = False
2849 de499029 Michael Hanselmann
2850 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2851 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2852 de499029 Michael Hanselmann
    """Actual signal handling function.
2853 de499029 Michael Hanselmann

2854 de499029 Michael Hanselmann
    """
2855 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2856 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2857 de499029 Michael Hanselmann
    self.called = True
2858 a2d2e1a7 Iustin Pop
2859 a2d2e1a7 Iustin Pop
2860 a2d2e1a7 Iustin Pop
class FieldSet(object):
2861 a2d2e1a7 Iustin Pop
  """A simple field set.
2862 a2d2e1a7 Iustin Pop

2863 a2d2e1a7 Iustin Pop
  Among the features are:
2864 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2865 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2866 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2867 a2d2e1a7 Iustin Pop

2868 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2869 a2d2e1a7 Iustin Pop

2870 a2d2e1a7 Iustin Pop
  """
2871 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2872 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2873 a2d2e1a7 Iustin Pop
2874 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2875 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2876 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2877 a2d2e1a7 Iustin Pop
2878 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2879 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2880 a2d2e1a7 Iustin Pop

2881 a2d2e1a7 Iustin Pop
    @type field: str
2882 a2d2e1a7 Iustin Pop
    @param field: the string to match
2883 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2884 a2d2e1a7 Iustin Pop

2885 a2d2e1a7 Iustin Pop
    """
2886 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2887 a2d2e1a7 Iustin Pop
      return m
2888 6c881c52 Iustin Pop
    return None
2889 a2d2e1a7 Iustin Pop
2890 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2891 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2892 a2d2e1a7 Iustin Pop

2893 a2d2e1a7 Iustin Pop
    @type items: list
2894 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2895 a2d2e1a7 Iustin Pop
    @rtype: list
2896 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2897 a2d2e1a7 Iustin Pop

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