Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ cc2f004d

History | View | Annotate | Download (78.1 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 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
369 a426508d Michael Hanselmann
370 6e797216 Michael Hanselmann
      return os.rename(old, new)
371 a426508d Michael Hanselmann
372 6e797216 Michael Hanselmann
    raise
373 6e797216 Michael Hanselmann
374 6e797216 Michael Hanselmann
375 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
376 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
377 76e5f8b5 Michael Hanselmann

378 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
379 76e5f8b5 Michael Hanselmann
  before Python 2.5.
380 76e5f8b5 Michael Hanselmann

381 76e5f8b5 Michael Hanselmann
  """
382 76e5f8b5 Michael Hanselmann
  try:
383 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
384 76e5f8b5 Michael Hanselmann
  except OSError, err:
385 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
386 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
387 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
388 76e5f8b5 Michael Hanselmann
      raise
389 76e5f8b5 Michael Hanselmann
390 76e5f8b5 Michael Hanselmann
391 055f822b Michael Hanselmann
def ResetTempfileModule():
392 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
393 055f822b Michael Hanselmann

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

400 055f822b Michael Hanselmann
  """
401 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
402 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
403 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
404 055f822b Michael Hanselmann
    try:
405 055f822b Michael Hanselmann
      # Reset random name generator
406 055f822b Michael Hanselmann
      tempfile._name_sequence = None
407 055f822b Michael Hanselmann
    finally:
408 055f822b Michael Hanselmann
      tempfile._once_lock.release()
409 055f822b Michael Hanselmann
  else:
410 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
411 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
412 055f822b Michael Hanselmann
413 055f822b Michael Hanselmann
414 a8083063 Iustin Pop
def _FingerprintFile(filename):
415 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
416 a8083063 Iustin Pop

417 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
418 a8083063 Iustin Pop
  instead.
419 a8083063 Iustin Pop

420 58885d79 Iustin Pop
  @type filename: str
421 58885d79 Iustin Pop
  @param filename: the filename to checksum
422 58885d79 Iustin Pop
  @rtype: str
423 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
424 58885d79 Iustin Pop
      of the file
425 a8083063 Iustin Pop

426 a8083063 Iustin Pop
  """
427 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
428 a8083063 Iustin Pop
    return None
429 a8083063 Iustin Pop
430 a8083063 Iustin Pop
  f = open(filename)
431 a8083063 Iustin Pop
432 7ffe8fba Carlos Valiente
  fp = sha1()
433 a8083063 Iustin Pop
  while True:
434 a8083063 Iustin Pop
    data = f.read(4096)
435 a8083063 Iustin Pop
    if not data:
436 a8083063 Iustin Pop
      break
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
    fp.update(data)
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
  return fp.hexdigest()
441 a8083063 Iustin Pop
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
def FingerprintFiles(files):
444 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
445 a8083063 Iustin Pop

446 58885d79 Iustin Pop
  @type files: list
447 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
448 58885d79 Iustin Pop
  @rtype: dict
449 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
450 58885d79 Iustin Pop
      existing files
451 a8083063 Iustin Pop

452 a8083063 Iustin Pop
  """
453 a8083063 Iustin Pop
  ret = {}
454 a8083063 Iustin Pop
455 a8083063 Iustin Pop
  for filename in files:
456 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
457 a8083063 Iustin Pop
    if cksum:
458 a8083063 Iustin Pop
      ret[filename] = cksum
459 a8083063 Iustin Pop
460 a8083063 Iustin Pop
  return ret
461 a8083063 Iustin Pop
462 a8083063 Iustin Pop
463 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
464 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
465 a5728081 Guido Trotter

466 a5728081 Guido Trotter
  @type target: dict
467 a5728081 Guido Trotter
  @param target: the dict to update
468 a5728081 Guido Trotter
  @type key_types: dict
469 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
470 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
471 a5728081 Guido Trotter
  @type allowed_values: list
472 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
473 a5728081 Guido Trotter

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

533 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
534 44bf25ff Iustin Pop
      will be returned as alive
535 58885d79 Iustin Pop
  @type pid: int
536 58885d79 Iustin Pop
  @param pid: the process ID to check
537 58885d79 Iustin Pop
  @rtype: boolean
538 58885d79 Iustin Pop
  @return: True if the process exists
539 a8083063 Iustin Pop

540 a8083063 Iustin Pop
  """
541 d9f311d7 Iustin Pop
  if pid <= 0:
542 d9f311d7 Iustin Pop
    return False
543 d9f311d7 Iustin Pop
544 a8083063 Iustin Pop
  try:
545 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
546 44bf25ff Iustin Pop
    return True
547 44bf25ff Iustin Pop
  except EnvironmentError, err:
548 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
549 a8083063 Iustin Pop
      return False
550 44bf25ff Iustin Pop
    raise
551 a8083063 Iustin Pop
552 a8083063 Iustin Pop
553 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
554 58885d79 Iustin Pop
  """Read a pid from a file.
555 fee80e90 Guido Trotter

556 58885d79 Iustin Pop
  @type  pidfile: string
557 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
558 58885d79 Iustin Pop
  @rtype: int
559 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
560 d9f311d7 Iustin Pop
           otherwise 0
561 fee80e90 Guido Trotter

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

582 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
583 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
584 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
585 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
586 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
587 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
588 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
589 a8083063 Iustin Pop

590 58885d79 Iustin Pop
  @type key: str
591 58885d79 Iustin Pop
  @param key: the name to be searched
592 58885d79 Iustin Pop
  @type name_list: list
593 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
594 256eb94b Guido Trotter
  @type case_sensitive: boolean
595 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
596 a8083063 Iustin Pop

597 58885d79 Iustin Pop
  @rtype: None or str
598 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
599 58885d79 Iustin Pop
      otherwise the element from the list which matches
600 a8083063 Iustin Pop

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

628 bcf043c9 Iustin Pop
  """
629 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
630 26288e68 Iustin Pop
631 89e1fc26 Iustin Pop
  def __init__(self, name=None):
632 bcf043c9 Iustin Pop
    """Initialize the host name object.
633 bcf043c9 Iustin Pop

634 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
635 89e1fc26 Iustin Pop
    name.
636 bcf043c9 Iustin Pop

637 bcf043c9 Iustin Pop
    """
638 89e1fc26 Iustin Pop
    if name is None:
639 89e1fc26 Iustin Pop
      name = self.SysName()
640 89e1fc26 Iustin Pop
641 89e1fc26 Iustin Pop
    self.query = name
642 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
643 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
644 bcf043c9 Iustin Pop
645 c8a0948f Michael Hanselmann
  def ShortName(self):
646 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
647 c8a0948f Michael Hanselmann

648 c8a0948f Michael Hanselmann
    """
649 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
650 c8a0948f Michael Hanselmann
651 89e1fc26 Iustin Pop
  @staticmethod
652 89e1fc26 Iustin Pop
  def SysName():
653 89e1fc26 Iustin Pop
    """Return the current system's name.
654 bcf043c9 Iustin Pop

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

657 89e1fc26 Iustin Pop
    """
658 89e1fc26 Iustin Pop
    return socket.gethostname()
659 a8083063 Iustin Pop
660 89e1fc26 Iustin Pop
  @staticmethod
661 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
662 89e1fc26 Iustin Pop
    """Look up hostname
663 a8083063 Iustin Pop

664 58885d79 Iustin Pop
    @type hostname: str
665 58885d79 Iustin Pop
    @param hostname: hostname to look up
666 89e1fc26 Iustin Pop

667 58885d79 Iustin Pop
    @rtype: tuple
668 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
669 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
670 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
671 89e1fc26 Iustin Pop

672 89e1fc26 Iustin Pop
    """
673 89e1fc26 Iustin Pop
    try:
674 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
675 89e1fc26 Iustin Pop
    except socket.gaierror, err:
676 89e1fc26 Iustin Pop
      # hostname not found in DNS
677 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
678 a8083063 Iustin Pop
679 89e1fc26 Iustin Pop
    return result
680 a8083063 Iustin Pop
681 26288e68 Iustin Pop
  @classmethod
682 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
683 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
684 26288e68 Iustin Pop

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

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

716 58885d79 Iustin Pop
  @rtype: dict
717 58885d79 Iustin Pop
  @return:
718 58885d79 Iustin Pop
       Dictionary with keys volume name and values
719 58885d79 Iustin Pop
       the size of the volume
720 a8083063 Iustin Pop

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

744 58885d79 Iustin Pop
  @type bridge: str
745 58885d79 Iustin Pop
  @param bridge: the bridge name to check
746 58885d79 Iustin Pop
  @rtype: boolean
747 58885d79 Iustin Pop
  @return: True if it does
748 a8083063 Iustin Pop

749 a8083063 Iustin Pop
  """
750 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
751 a8083063 Iustin Pop
752 a8083063 Iustin Pop
753 a8083063 Iustin Pop
def NiceSort(name_list):
754 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
755 a8083063 Iustin Pop

756 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
757 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
758 58885d79 Iustin Pop
  'a11']}.
759 a8083063 Iustin Pop

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

764 58885d79 Iustin Pop
  @type name_list: list
765 58885d79 Iustin Pop
  @param name_list: the names to be sorted
766 58885d79 Iustin Pop
  @rtype: list
767 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
768 a8083063 Iustin Pop

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

793 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
794 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
795 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
796 58885d79 Iustin Pop
  exceptions are propagated to the caller.
797 58885d79 Iustin Pop

798 58885d79 Iustin Pop
  @type fn: callable
799 58885d79 Iustin Pop
  @param fn: function to apply to the value
800 58885d79 Iustin Pop
  @param val: the value to be converted
801 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
802 58885d79 Iustin Pop
      otherwise the original value.
803 a8083063 Iustin Pop

804 a8083063 Iustin Pop
  """
805 a8083063 Iustin Pop
  try:
806 a8083063 Iustin Pop
    nv = fn(val)
807 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
808 a8083063 Iustin Pop
    nv = val
809 a8083063 Iustin Pop
  return nv
810 a8083063 Iustin Pop
811 a8083063 Iustin Pop
812 a8083063 Iustin Pop
def IsValidIP(ip):
813 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
814 a8083063 Iustin Pop

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

818 58885d79 Iustin Pop
  @type ip: str
819 58885d79 Iustin Pop
  @param ip: the address to be checked
820 58885d79 Iustin Pop
  @rtype: a regular expression match object
821 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
822 58885d79 Iustin Pop
      address is not valid
823 a8083063 Iustin Pop

824 a8083063 Iustin Pop
  """
825 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
826 58885d79 Iustin Pop
  #TODO: convert and return only boolean
827 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
828 a8083063 Iustin Pop
829 a8083063 Iustin Pop
830 a8083063 Iustin Pop
def IsValidShellParam(word):
831 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
832 a8083063 Iustin Pop

833 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
834 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
835 a8083063 Iustin Pop
  the actual command.
836 a8083063 Iustin Pop

837 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
838 a8083063 Iustin Pop
  side.
839 a8083063 Iustin Pop

840 58885d79 Iustin Pop
  @type word: str
841 58885d79 Iustin Pop
  @param word: the word to check
842 58885d79 Iustin Pop
  @rtype: boolean
843 58885d79 Iustin Pop
  @return: True if the word is 'safe'
844 58885d79 Iustin Pop

845 a8083063 Iustin Pop
  """
846 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
847 a8083063 Iustin Pop
848 a8083063 Iustin Pop
849 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
850 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
851 a8083063 Iustin Pop

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

857 58885d79 Iustin Pop
  @type template: str
858 58885d79 Iustin Pop
  @param template: the string holding the template for the
859 58885d79 Iustin Pop
      string formatting
860 58885d79 Iustin Pop
  @rtype: str
861 58885d79 Iustin Pop
  @return: the expanded command line
862 58885d79 Iustin Pop

863 a8083063 Iustin Pop
  """
864 a8083063 Iustin Pop
  for word in args:
865 a8083063 Iustin Pop
    if not IsValidShellParam(word):
866 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
867 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
868 a8083063 Iustin Pop
  return template % args
869 a8083063 Iustin Pop
870 a8083063 Iustin Pop
871 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
872 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
873 a8083063 Iustin Pop

874 58885d79 Iustin Pop
  @type value: int
875 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
876 9fbfbb7b Iustin Pop
  @type units: char
877 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
878 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
879 9fbfbb7b Iustin Pop
      - 'm' for MiBs
880 9fbfbb7b Iustin Pop
      - 'g' for GiBs
881 9fbfbb7b Iustin Pop
      - 't' for TiBs
882 58885d79 Iustin Pop
  @rtype: str
883 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
884 a8083063 Iustin Pop

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

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

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

955 58885d79 Iustin Pop
  @type file_name: str
956 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
957 58885d79 Iustin Pop
  @type key: str
958 58885d79 Iustin Pop
  @param key: string containing key
959 58885d79 Iustin Pop

960 a8083063 Iustin Pop
  """
961 a8083063 Iustin Pop
  key_fields = key.split()
962 a8083063 Iustin Pop
963 a8083063 Iustin Pop
  f = open(file_name, 'a+')
964 a8083063 Iustin Pop
  try:
965 a8083063 Iustin Pop
    nl = True
966 a8083063 Iustin Pop
    for line in f:
967 a8083063 Iustin Pop
      # Ignore whitespace changes
968 a8083063 Iustin Pop
      if line.split() == key_fields:
969 a8083063 Iustin Pop
        break
970 a8083063 Iustin Pop
      nl = line.endswith('\n')
971 a8083063 Iustin Pop
    else:
972 a8083063 Iustin Pop
      if not nl:
973 a8083063 Iustin Pop
        f.write("\n")
974 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
975 a8083063 Iustin Pop
      f.write("\n")
976 a8083063 Iustin Pop
      f.flush()
977 a8083063 Iustin Pop
  finally:
978 a8083063 Iustin Pop
    f.close()
979 a8083063 Iustin Pop
980 a8083063 Iustin Pop
981 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
982 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
983 a8083063 Iustin Pop

984 58885d79 Iustin Pop
  @type file_name: str
985 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
986 58885d79 Iustin Pop
  @type key: str
987 58885d79 Iustin Pop
  @param key: string containing key
988 58885d79 Iustin Pop

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

1017 58885d79 Iustin Pop
  @type file_name: str
1018 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1019 58885d79 Iustin Pop
  @type ip: str
1020 58885d79 Iustin Pop
  @param ip: the IP address
1021 58885d79 Iustin Pop
  @type hostname: str
1022 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1023 58885d79 Iustin Pop
  @type aliases: list
1024 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1025 58885d79 Iustin Pop

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

1064 58885d79 Iustin Pop
  @type hostname: str
1065 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1066 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1067 58885d79 Iustin Pop

1068 d9c02ca6 Michael Hanselmann
  """
1069 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1070 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1071 d9c02ca6 Michael Hanselmann
1072 d9c02ca6 Michael Hanselmann
1073 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1074 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1075 899d2a81 Michael Hanselmann

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

1078 58885d79 Iustin Pop
  @type file_name: str
1079 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1080 58885d79 Iustin Pop
  @type hostname: str
1081 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1082 58885d79 Iustin Pop

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

1120 58885d79 Iustin Pop
  @type hostname: str
1121 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1122 58885d79 Iustin Pop
      full and shot name will be removed from
1123 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1124 58885d79 Iustin Pop

1125 d9c02ca6 Michael Hanselmann
  """
1126 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1127 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1128 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1129 d9c02ca6 Michael Hanselmann
1130 d9c02ca6 Michael Hanselmann
1131 1d466a4f Michael Hanselmann
def TimestampForFilename():
1132 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1133 1d466a4f Michael Hanselmann

1134 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1135 1d466a4f Michael Hanselmann
  separators.
1136 1d466a4f Michael Hanselmann

1137 1d466a4f Michael Hanselmann
  """
1138 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1139 1d466a4f Michael Hanselmann
1140 1d466a4f Michael Hanselmann
1141 a8083063 Iustin Pop
def CreateBackup(file_name):
1142 a8083063 Iustin Pop
  """Creates a backup of a file.
1143 a8083063 Iustin Pop

1144 58885d79 Iustin Pop
  @type file_name: str
1145 58885d79 Iustin Pop
  @param file_name: file to be backed up
1146 58885d79 Iustin Pop
  @rtype: str
1147 58885d79 Iustin Pop
  @return: the path to the newly created backup
1148 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1149 a8083063 Iustin Pop

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

1177 58885d79 Iustin Pop
  @type value: str
1178 58885d79 Iustin Pop
  @param value: the argument to be quoted
1179 58885d79 Iustin Pop
  @rtype: str
1180 58885d79 Iustin Pop
  @return: the quoted value
1181 58885d79 Iustin Pop

1182 a8083063 Iustin Pop
  """
1183 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1184 a8083063 Iustin Pop
    return value
1185 a8083063 Iustin Pop
  else:
1186 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1187 a8083063 Iustin Pop
1188 a8083063 Iustin Pop
1189 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1190 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1191 58885d79 Iustin Pop

1192 58885d79 Iustin Pop
  @type args: list
1193 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1194 58885d79 Iustin Pop
  @rtype: str
1195 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1196 a8083063 Iustin Pop

1197 a8083063 Iustin Pop
  """
1198 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1199 88d14415 Michael Hanselmann
1200 88d14415 Michael Hanselmann
1201 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1202 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1203 2c30e9d7 Alexander Schreiber

1204 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1205 58885d79 Iustin Pop
  to it.
1206 58885d79 Iustin Pop

1207 58885d79 Iustin Pop
  @type target: str
1208 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1209 58885d79 Iustin Pop
  @type port: int
1210 58885d79 Iustin Pop
  @param port: the port to connect to
1211 58885d79 Iustin Pop
  @type timeout: int
1212 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1213 58885d79 Iustin Pop
  @type live_port_needed: boolean
1214 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1215 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1216 58885d79 Iustin Pop
  @type source: str or None
1217 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1218 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1219 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1220 2c30e9d7 Alexander Schreiber

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

1250 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1251 caad16e2 Iustin Pop
  address.
1252 caad16e2 Iustin Pop

1253 caad16e2 Iustin Pop
  @type address: string
1254 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1255 caad16e2 Iustin Pop
  @rtype: bool
1256 58885d79 Iustin Pop
  @return: True if we own the address
1257 caad16e2 Iustin Pop

1258 caad16e2 Iustin Pop
  """
1259 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1260 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1261 caad16e2 Iustin Pop
1262 caad16e2 Iustin Pop
1263 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1264 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1265 58885d79 Iustin Pop

1266 58885d79 Iustin Pop
  @type path: str
1267 58885d79 Iustin Pop
  @param path: the directory to enumerate
1268 58885d79 Iustin Pop
  @rtype: list
1269 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1270 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1271 eedbda4b Michael Hanselmann

1272 eedbda4b Michael Hanselmann
  """
1273 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1274 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1275 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1276 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1277 f3299a07 Michael Hanselmann
  files.sort()
1278 f3299a07 Michael Hanselmann
  return files
1279 2f8b60b3 Iustin Pop
1280 2f8b60b3 Iustin Pop
1281 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1282 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1283 257f4c0a Iustin Pop

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

1288 2f8b60b3 Iustin Pop
  """
1289 2f8b60b3 Iustin Pop
  try:
1290 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1291 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1292 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1293 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1294 257f4c0a Iustin Pop
    else:
1295 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1296 257f4c0a Iustin Pop
                                   type(user))
1297 2f8b60b3 Iustin Pop
  except KeyError:
1298 2f8b60b3 Iustin Pop
    return default
1299 2f8b60b3 Iustin Pop
  return result.pw_dir
1300 59072e7e Michael Hanselmann
1301 59072e7e Michael Hanselmann
1302 24818e8f Michael Hanselmann
def NewUUID():
1303 59072e7e Michael Hanselmann
  """Returns a random UUID.
1304 59072e7e Michael Hanselmann

1305 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1306 58885d79 Iustin Pop
      filesystem.
1307 58885d79 Iustin Pop
  @rtype: str
1308 58885d79 Iustin Pop

1309 59072e7e Michael Hanselmann
  """
1310 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1311 087b34fe Iustin Pop
1312 087b34fe Iustin Pop
1313 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1314 33081d90 Iustin Pop
  """Generates a random secret.
1315 33081d90 Iustin Pop

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

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

1324 33081d90 Iustin Pop
  """
1325 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1326 33081d90 Iustin Pop
1327 33081d90 Iustin Pop
1328 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1329 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1330 9dae41ad Guido Trotter

1331 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1332 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1333 9dae41ad Guido Trotter

1334 9dae41ad Guido Trotter
  """
1335 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1336 9dae41ad Guido Trotter
    try:
1337 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1338 9dae41ad Guido Trotter
    except EnvironmentError, err:
1339 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1340 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1341 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1342 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1343 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1344 9dae41ad Guido Trotter
1345 9dae41ad Guido Trotter
1346 016308cb Iustin Pop
def ReadFile(file_name, size=-1):
1347 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1348 ca0aa6d0 Michael Hanselmann

1349 016308cb Iustin Pop
  @type size: int
1350 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1351 58885d79 Iustin Pop
  @rtype: str
1352 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1353 ca0aa6d0 Michael Hanselmann

1354 ca0aa6d0 Michael Hanselmann
  """
1355 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1356 ca0aa6d0 Michael Hanselmann
  try:
1357 016308cb Iustin Pop
    return f.read(size)
1358 ca0aa6d0 Michael Hanselmann
  finally:
1359 ca0aa6d0 Michael Hanselmann
    f.close()
1360 ca0aa6d0 Michael Hanselmann
1361 ca0aa6d0 Michael Hanselmann
1362 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1363 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1364 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1365 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1366 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1367 087b34fe Iustin Pop
  """(Over)write a file atomically.
1368 087b34fe Iustin Pop

1369 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1370 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1371 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1372 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1373 087b34fe Iustin Pop
  mtime/atime of the file.
1374 087b34fe Iustin Pop

1375 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1376 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1377 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1378 087b34fe Iustin Pop
  temporary file should be removed.
1379 087b34fe Iustin Pop

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

1404 58885d79 Iustin Pop
  @rtype: None or int
1405 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1406 58885d79 Iustin Pop
      otherwise the file descriptor
1407 58885d79 Iustin Pop

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

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

1464 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1465 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1466 7b4126b7 Iustin Pop
  value, the index will be returned.
1467 7b4126b7 Iustin Pop

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

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

1473 58885d79 Iustin Pop
  @type seq: sequence
1474 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1475 58885d79 Iustin Pop
  @type base: int
1476 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1477 58885d79 Iustin Pop
  @rtype: int
1478 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1479 7b4126b7 Iustin Pop

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

1506 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1507 dfdc4060 Guido Trotter

1508 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1509 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1510 dfdc4060 Guido Trotter
  @type event: integer
1511 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1512 dcd511c8 Guido Trotter
  @type timeout: float or None
1513 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1514 dcd511c8 Guido Trotter
  @rtype: int or None
1515 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1516 dcd511c8 Guido Trotter

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

1545 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1546 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1547 dfdc4060 Guido Trotter
  expired.
1548 dfdc4060 Guido Trotter

1549 dfdc4060 Guido Trotter
  """
1550 dfdc4060 Guido Trotter
1551 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1552 dfdc4060 Guido Trotter
    self.timeout = timeout
1553 dfdc4060 Guido Trotter
1554 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1555 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1556 dfdc4060 Guido Trotter
    if result is None:
1557 dfdc4060 Guido Trotter
      raise RetryAgain()
1558 dfdc4060 Guido Trotter
    else:
1559 dfdc4060 Guido Trotter
      return result
1560 dfdc4060 Guido Trotter
1561 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1562 dfdc4060 Guido Trotter
    self.timeout = timeout
1563 dfdc4060 Guido Trotter
1564 dfdc4060 Guido Trotter
1565 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1566 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1567 dfdc4060 Guido Trotter

1568 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1569 dfdc4060 Guido Trotter

1570 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1571 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1572 dfdc4060 Guido Trotter
  @type event: integer
1573 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1574 dfdc4060 Guido Trotter
  @type timeout: float or None
1575 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1576 dfdc4060 Guido Trotter
  @rtype: int or None
1577 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1578 dfdc4060 Guido Trotter

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

1600 f7414041 Michael Hanselmann
  Element order is preserved.
1601 58885d79 Iustin Pop

1602 58885d79 Iustin Pop
  @type seq: sequence
1603 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1604 58885d79 Iustin Pop
  @rtype: list
1605 58885d79 Iustin Pop
  @return: list of unique elements from seq
1606 58885d79 Iustin Pop

1607 f7414041 Michael Hanselmann
  """
1608 f7414041 Michael Hanselmann
  seen = set()
1609 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1610 1862d460 Alexander Schreiber
1611 1862d460 Alexander Schreiber
1612 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1613 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1614 1862d460 Alexander Schreiber

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

1618 58885d79 Iustin Pop
  @type mac: str
1619 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1620 82187135 Renรฉ Nussbaumer
  @rtype: str
1621 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1622 82187135 Renรฉ Nussbaumer

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

1625 1862d460 Alexander Schreiber
  """
1626 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1627 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1628 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1629 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1630 82187135 Renรฉ Nussbaumer
1631 82187135 Renรฉ Nussbaumer
  return mac.lower()
1632 06009e27 Iustin Pop
1633 06009e27 Iustin Pop
1634 06009e27 Iustin Pop
def TestDelay(duration):
1635 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1636 06009e27 Iustin Pop

1637 58885d79 Iustin Pop
  @type duration: float
1638 58885d79 Iustin Pop
  @param duration: the sleep duration
1639 58885d79 Iustin Pop
  @rtype: boolean
1640 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1641 58885d79 Iustin Pop

1642 06009e27 Iustin Pop
  """
1643 06009e27 Iustin Pop
  if duration < 0:
1644 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1645 06009e27 Iustin Pop
  time.sleep(duration)
1646 38ea42a1 Iustin Pop
  return True, None
1647 8f765069 Iustin Pop
1648 8f765069 Iustin Pop
1649 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1650 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1651 8f765069 Iustin Pop

1652 7d88772a Iustin Pop
  @type fd: int
1653 7d88772a Iustin Pop
  @param fd: the file descriptor
1654 7d88772a Iustin Pop
  @type retries: int
1655 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1656 7d88772a Iustin Pop
      other error than EBADF
1657 7d88772a Iustin Pop

1658 7d88772a Iustin Pop
  """
1659 7d88772a Iustin Pop
  try:
1660 7d88772a Iustin Pop
    os.close(fd)
1661 7d88772a Iustin Pop
  except OSError, err:
1662 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1663 7d88772a Iustin Pop
      if retries > 0:
1664 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1665 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1666 7d88772a Iustin Pop
    # ignore this and go on
1667 7d88772a Iustin Pop
1668 7d88772a Iustin Pop
1669 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1670 7d88772a Iustin Pop
  """Close file descriptors.
1671 7d88772a Iustin Pop

1672 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1673 7d88772a Iustin Pop
  stdin/out/err).
1674 8f765069 Iustin Pop

1675 58885d79 Iustin Pop
  @type noclose_fds: list or None
1676 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1677 58885d79 Iustin Pop
      that should not be closed
1678 58885d79 Iustin Pop

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

1704 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1705 7d88772a Iustin Pop
  runs it in the background as a daemon.
1706 7d88772a Iustin Pop

1707 7d88772a Iustin Pop
  @type logfile: str
1708 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1709 7d88772a Iustin Pop
  @rtype: int
1710 5fcc718f Iustin Pop
  @return: the value zero
1711 7d88772a Iustin Pop

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

1747 58885d79 Iustin Pop
  @type name: str
1748 58885d79 Iustin Pop
  @param name: the daemon name
1749 58885d79 Iustin Pop
  @rtype: str
1750 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1751 58885d79 Iustin Pop
      daemon name
1752 b330ac0b Guido Trotter

1753 b330ac0b Guido Trotter
  """
1754 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1755 b330ac0b Guido Trotter
1756 b330ac0b Guido Trotter
1757 2826b361 Guido Trotter
def EnsureDaemon(name):
1758 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1759 2826b361 Guido Trotter

1760 2826b361 Guido Trotter
  """
1761 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1762 2826b361 Guido Trotter
  if result.failed:
1763 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1764 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1765 2826b361 Guido Trotter
    return False
1766 2826b361 Guido Trotter
1767 2826b361 Guido Trotter
  return True
1768 2826b361 Guido Trotter
1769 2826b361 Guido Trotter
1770 b330ac0b Guido Trotter
def WritePidFile(name):
1771 b330ac0b Guido Trotter
  """Write the current process pidfile.
1772 b330ac0b Guido Trotter

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

1775 58885d79 Iustin Pop
  @type name: str
1776 58885d79 Iustin Pop
  @param name: the daemon name to use
1777 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1778 58885d79 Iustin Pop
      points to a live process
1779 b330ac0b Guido Trotter

1780 b330ac0b Guido Trotter
  """
1781 b330ac0b Guido Trotter
  pid = os.getpid()
1782 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1783 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1784 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1785 b330ac0b Guido Trotter
1786 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1787 b330ac0b Guido Trotter
1788 b330ac0b Guido Trotter
1789 b330ac0b Guido Trotter
def RemovePidFile(name):
1790 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1791 b330ac0b Guido Trotter

1792 b330ac0b Guido Trotter
  Any errors are ignored.
1793 b330ac0b Guido Trotter

1794 58885d79 Iustin Pop
  @type name: str
1795 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1796 58885d79 Iustin Pop

1797 b330ac0b Guido Trotter
  """
1798 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1799 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1800 b330ac0b Guido Trotter
  try:
1801 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1802 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1803 b330ac0b Guido Trotter
    pass
1804 b330ac0b Guido Trotter
1805 b330ac0b Guido Trotter
1806 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1807 ff5251bc Iustin Pop
                waitpid=False):
1808 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1809 b2a1f511 Iustin Pop

1810 b2a1f511 Iustin Pop
  @type pid: int
1811 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1812 38206f3c Iustin Pop
  @type signal_: int
1813 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1814 b2a1f511 Iustin Pop
  @type timeout: int
1815 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1816 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1817 b2a1f511 Iustin Pop
                  will be done
1818 ff5251bc Iustin Pop
  @type waitpid: boolean
1819 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1820 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1821 ff5251bc Iustin Pop
      would remain as zombie
1822 b2a1f511 Iustin Pop

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

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

1876 58885d79 Iustin Pop
  @type name: str
1877 58885d79 Iustin Pop
  @param name: the name to look for
1878 58885d79 Iustin Pop
  @type search_path: str
1879 58885d79 Iustin Pop
  @param search_path: location to start at
1880 58885d79 Iustin Pop
  @type test: callable
1881 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1882 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1883 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1884 58885d79 Iustin Pop
  @rtype: str or None
1885 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1886 57c177af Iustin Pop

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

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

1910 58885d79 Iustin Pop
  @type vglist: dict
1911 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1912 58885d79 Iustin Pop
  @type vgname: str
1913 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1914 58885d79 Iustin Pop
  @type minsize: int
1915 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1916 58885d79 Iustin Pop
  @rtype: None or str
1917 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1918 8d1a2a64 Michael Hanselmann

1919 8d1a2a64 Michael Hanselmann
  """
1920 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1921 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1922 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1923 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1924 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1925 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1926 8d1a2a64 Michael Hanselmann
  return None
1927 7996a135 Iustin Pop
1928 7996a135 Iustin Pop
1929 45bc5e4a Michael Hanselmann
def SplitTime(value):
1930 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1931 739be818 Michael Hanselmann

1932 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1933 45bc5e4a Michael Hanselmann
  @type value: int or float
1934 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1935 739be818 Michael Hanselmann

1936 739be818 Michael Hanselmann
  """
1937 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1938 45bc5e4a Michael Hanselmann
1939 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1940 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1941 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1942 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1943 45bc5e4a Michael Hanselmann
1944 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1945 739be818 Michael Hanselmann
1946 739be818 Michael Hanselmann
1947 739be818 Michael Hanselmann
def MergeTime(timetuple):
1948 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1949 739be818 Michael Hanselmann

1950 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1951 739be818 Michael Hanselmann
  @type timetuple: tuple
1952 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1953 739be818 Michael Hanselmann

1954 739be818 Michael Hanselmann
  """
1955 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1956 739be818 Michael Hanselmann
1957 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1958 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1959 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1960 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1961 739be818 Michael Hanselmann
1962 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1963 739be818 Michael Hanselmann
1964 739be818 Michael Hanselmann
1965 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1966 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1967 4a8b186a Michael Hanselmann

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

1972 cd50653c Guido Trotter
  @type daemon_name: string
1973 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1974 58885d79 Iustin Pop
  @rtype: int
1975 58885d79 Iustin Pop

1976 4a8b186a Michael Hanselmann
  """
1977 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1978 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1979 cd50653c Guido Trotter
1980 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1981 4a8b186a Michael Hanselmann
  try:
1982 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1983 4a8b186a Michael Hanselmann
  except socket.error:
1984 cd50653c Guido Trotter
    port = default_port
1985 4a8b186a Michael Hanselmann
1986 4a8b186a Michael Hanselmann
  return port
1987 4a8b186a Michael Hanselmann
1988 4a8b186a Michael Hanselmann
1989 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
1990 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
1991 82d9caef Iustin Pop
  """Configures the logging module.
1992 82d9caef Iustin Pop

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

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

2078 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2079 da961187 Guido Trotter

2080 da961187 Guido Trotter
  """
2081 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2082 82d9caef Iustin Pop
2083 016d04b3 Michael Hanselmann
2084 4bb678e9 Iustin Pop
def PathJoin(*args):
2085 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2086 4bb678e9 Iustin Pop

2087 4bb678e9 Iustin Pop
  Requirements:
2088 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2089 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2090 4bb678e9 Iustin Pop
        since we check for normalization at the end
2091 4bb678e9 Iustin Pop

2092 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2093 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2094 4bb678e9 Iustin Pop

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

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

2121 f65f63ef Iustin Pop
  @param fname: the file name
2122 f65f63ef Iustin Pop
  @type lines: int
2123 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2124 f65f63ef Iustin Pop

2125 f65f63ef Iustin Pop
  """
2126 f65f63ef Iustin Pop
  fd = open(fname, "r")
2127 f65f63ef Iustin Pop
  try:
2128 f65f63ef Iustin Pop
    fd.seek(0, 2)
2129 f65f63ef Iustin Pop
    pos = fd.tell()
2130 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2131 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2132 f65f63ef Iustin Pop
    raw_data = fd.read()
2133 f65f63ef Iustin Pop
  finally:
2134 f65f63ef Iustin Pop
    fd.close()
2135 f65f63ef Iustin Pop
2136 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2137 f65f63ef Iustin Pop
  return rows[-lines:]
2138 f65f63ef Iustin Pop
2139 f65f63ef Iustin Pop
2140 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2141 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2142 27e46076 Michael Hanselmann

2143 27e46076 Michael Hanselmann
  @type value: string
2144 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2145 27e46076 Michael Hanselmann

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

2170 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2171 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2172 27e46076 Michael Hanselmann

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

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

2215 26f15862 Iustin Pop
  @type text: str or unicode
2216 26f15862 Iustin Pop
  @param text: input data
2217 26f15862 Iustin Pop
  @rtype: str
2218 26f15862 Iustin Pop
  @return: a safe version of text
2219 26f15862 Iustin Pop

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

2243 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2244 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2245 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2246 5b69bc7c Iustin Pop
  separator):
2247 5b69bc7c Iustin Pop
    - a plain , separates the elements
2248 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2249 5b69bc7c Iustin Pop
      backslash plus a separator comma
2250 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2251 5b69bc7c Iustin Pop
      non-separator comma
2252 5b69bc7c Iustin Pop

2253 5b69bc7c Iustin Pop
  @type text: string
2254 5b69bc7c Iustin Pop
  @param text: the string to split
2255 5b69bc7c Iustin Pop
  @type sep: string
2256 5b69bc7c Iustin Pop
  @param text: the separator
2257 5b69bc7c Iustin Pop
  @rtype: string
2258 5b69bc7c Iustin Pop
  @return: a list of strings
2259 5b69bc7c Iustin Pop

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

2285 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2286 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2287 ab3e6da8 Iustin Pop

2288 ab3e6da8 Iustin Pop
  """
2289 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2290 ab3e6da8 Iustin Pop
2291 ab3e6da8 Iustin Pop
2292 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2293 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2294 3f6a47a8 Michael Hanselmann

2295 3f6a47a8 Michael Hanselmann
  @type value: int
2296 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2297 3f6a47a8 Michael Hanselmann
  @rtype: int
2298 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2299 3f6a47a8 Michael Hanselmann

2300 3f6a47a8 Michael Hanselmann
  """
2301 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2302 3f6a47a8 Michael Hanselmann
2303 3f6a47a8 Michael Hanselmann
2304 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2305 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2306 3f6a47a8 Michael Hanselmann

2307 3f6a47a8 Michael Hanselmann
  @type path: string
2308 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2309 3f6a47a8 Michael Hanselmann
  @rtype: int
2310 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2311 3f6a47a8 Michael Hanselmann

2312 3f6a47a8 Michael Hanselmann
  """
2313 3f6a47a8 Michael Hanselmann
  size = 0
2314 3f6a47a8 Michael Hanselmann
2315 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2316 2a887df9 Michael Hanselmann
    for filename in files:
2317 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2318 3f6a47a8 Michael Hanselmann
      size += st.st_size
2319 3f6a47a8 Michael Hanselmann
2320 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2321 3f6a47a8 Michael Hanselmann
2322 3f6a47a8 Michael Hanselmann
2323 620a85fd Iustin Pop
def GetFilesystemStats(path):
2324 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2325 3f6a47a8 Michael Hanselmann

2326 3f6a47a8 Michael Hanselmann
  @type path: string
2327 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2328 3f6a47a8 Michael Hanselmann
  @rtype: int
2329 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2330 3f6a47a8 Michael Hanselmann

2331 3f6a47a8 Michael Hanselmann
  """
2332 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2333 3f6a47a8 Michael Hanselmann
2334 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2335 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2336 620a85fd Iustin Pop
  return (tsize, fsize)
2337 3f6a47a8 Michael Hanselmann
2338 3f6a47a8 Michael Hanselmann
2339 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2340 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2341 eb58f7bd Michael Hanselmann

2342 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2343 eb58f7bd Michael Hanselmann

2344 eb58f7bd Michael Hanselmann
  @type fn: callable
2345 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2346 bdefe5dd Michael Hanselmann
  @rtype: bool
2347 bdefe5dd Michael Hanselmann
  @return: Function's result
2348 eb58f7bd Michael Hanselmann

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

2389 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2390 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2391 7996a135 Iustin Pop

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

2417 58885d79 Iustin Pop
  @type fd: int
2418 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2419 58885d79 Iustin Pop

2420 eb0f0ce0 Michael Hanselmann
  """
2421 eb0f0ce0 Michael Hanselmann
  try:
2422 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2423 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2424 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2425 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2426 eb0f0ce0 Michael Hanselmann
    raise
2427 de499029 Michael Hanselmann
2428 de499029 Michael Hanselmann
2429 3b813dd2 Iustin Pop
def FormatTime(val):
2430 3b813dd2 Iustin Pop
  """Formats a time value.
2431 3b813dd2 Iustin Pop

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

2436 3b813dd2 Iustin Pop
  """
2437 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2438 3b813dd2 Iustin Pop
    return "N/A"
2439 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2440 3b813dd2 Iustin Pop
  # platforms
2441 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2442 3b813dd2 Iustin Pop
2443 3b813dd2 Iustin Pop
2444 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2445 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2446 05e50653 Michael Hanselmann

2447 5cbe43a5 Michael Hanselmann
  @type filename: string
2448 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2449 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2450 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2451 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2452 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2453 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2454 5cbe43a5 Michael Hanselmann

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

2490 de0ea66b Michael Hanselmann
  """
2491 de0ea66b Michael Hanselmann
2492 de0ea66b Michael Hanselmann
2493 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2494 de0ea66b Michael Hanselmann
  """Retry again.
2495 de0ea66b Michael Hanselmann

2496 de0ea66b Michael Hanselmann
  """
2497 de0ea66b Michael Hanselmann
2498 de0ea66b Michael Hanselmann
2499 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2500 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2501 de0ea66b Michael Hanselmann

2502 de0ea66b Michael Hanselmann
  """
2503 de0ea66b Michael Hanselmann
  __slots__ = [
2504 de0ea66b Michael Hanselmann
    "_factor",
2505 de0ea66b Michael Hanselmann
    "_limit",
2506 de0ea66b Michael Hanselmann
    "_next",
2507 de0ea66b Michael Hanselmann
    "_start",
2508 de0ea66b Michael Hanselmann
    ]
2509 de0ea66b Michael Hanselmann
2510 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2511 de0ea66b Michael Hanselmann
    """Initializes this class.
2512 de0ea66b Michael Hanselmann

2513 de0ea66b Michael Hanselmann
    @type start: float
2514 de0ea66b Michael Hanselmann
    @param start: Initial delay
2515 de0ea66b Michael Hanselmann
    @type factor: float
2516 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2517 de0ea66b Michael Hanselmann
    @type limit: float or None
2518 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2519 de0ea66b Michael Hanselmann

2520 de0ea66b Michael Hanselmann
    """
2521 de0ea66b Michael Hanselmann
    assert start > 0.0
2522 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2523 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2524 de0ea66b Michael Hanselmann
2525 de0ea66b Michael Hanselmann
    self._start = start
2526 de0ea66b Michael Hanselmann
    self._factor = factor
2527 de0ea66b Michael Hanselmann
    self._limit = limit
2528 de0ea66b Michael Hanselmann
2529 de0ea66b Michael Hanselmann
    self._next = start
2530 de0ea66b Michael Hanselmann
2531 de0ea66b Michael Hanselmann
  def __call__(self):
2532 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2533 de0ea66b Michael Hanselmann

2534 de0ea66b Michael Hanselmann
    """
2535 de0ea66b Michael Hanselmann
    current = self._next
2536 de0ea66b Michael Hanselmann
2537 de0ea66b Michael Hanselmann
    # Update for next run
2538 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2539 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2540 de0ea66b Michael Hanselmann
2541 de0ea66b Michael Hanselmann
    return current
2542 de0ea66b Michael Hanselmann
2543 de0ea66b Michael Hanselmann
2544 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2545 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2546 de0ea66b Michael Hanselmann
2547 de0ea66b Michael Hanselmann
2548 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2549 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2550 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2551 de0ea66b Michael Hanselmann

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

2556 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2557 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2558 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2559 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2560 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2561 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2562 de0ea66b Michael Hanselmann

2563 de0ea66b Michael Hanselmann
  @type fn: callable
2564 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2565 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2566 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2567 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2568 de0ea66b Michael Hanselmann
  @type timeout: float
2569 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2570 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2571 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2572 de0ea66b Michael Hanselmann
  @return: Return value of function
2573 de0ea66b Michael Hanselmann

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

2628 a87b4824 Michael Hanselmann
  """
2629 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2630 58885d79 Iustin Pop
    """Constructor for FileLock.
2631 58885d79 Iustin Pop

2632 b4478d34 Michael Hanselmann
    @type fd: file
2633 b4478d34 Michael Hanselmann
    @param fd: File object
2634 58885d79 Iustin Pop
    @type filename: str
2635 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2636 58885d79 Iustin Pop

2637 58885d79 Iustin Pop
    """
2638 b4478d34 Michael Hanselmann
    self.fd = fd
2639 a87b4824 Michael Hanselmann
    self.filename = filename
2640 b4478d34 Michael Hanselmann
2641 b4478d34 Michael Hanselmann
  @classmethod
2642 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2643 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2644 b4478d34 Michael Hanselmann

2645 b4478d34 Michael Hanselmann
    @type filename: string
2646 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2647 b4478d34 Michael Hanselmann

2648 b4478d34 Michael Hanselmann
    """
2649 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2650 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2651 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2652 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2653 b4478d34 Michael Hanselmann
               filename)
2654 a87b4824 Michael Hanselmann
2655 a87b4824 Michael Hanselmann
  def __del__(self):
2656 a87b4824 Michael Hanselmann
    self.Close()
2657 a87b4824 Michael Hanselmann
2658 a87b4824 Michael Hanselmann
  def Close(self):
2659 58885d79 Iustin Pop
    """Close the file and release the lock.
2660 58885d79 Iustin Pop

2661 58885d79 Iustin Pop
    """
2662 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2663 a87b4824 Michael Hanselmann
      self.fd.close()
2664 a87b4824 Michael Hanselmann
      self.fd = None
2665 a87b4824 Michael Hanselmann
2666 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2667 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2668 aa74b828 Michael Hanselmann

2669 aa74b828 Michael Hanselmann
    @type flag: int
2670 58885d79 Iustin Pop
    @param flag: operation flag
2671 aa74b828 Michael Hanselmann
    @type blocking: bool
2672 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2673 aa74b828 Michael Hanselmann
    @type timeout: None or float
2674 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2675 aa74b828 Michael Hanselmann
                    non-blocking mode).
2676 aa74b828 Michael Hanselmann
    @type errmsg: string
2677 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2678 aa74b828 Michael Hanselmann

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

2712 58885d79 Iustin Pop
    @type blocking: boolean
2713 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2714 58885d79 Iustin Pop
        can lock the file or return immediately
2715 58885d79 Iustin Pop
    @type timeout: int or None
2716 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2717 58885d79 Iustin Pop
        (in blocking mode)
2718 58885d79 Iustin Pop

2719 a87b4824 Michael Hanselmann
    """
2720 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2721 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2722 a87b4824 Michael Hanselmann
2723 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2724 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2725 a87b4824 Michael Hanselmann

2726 58885d79 Iustin Pop
    @type blocking: boolean
2727 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2728 58885d79 Iustin Pop
        can lock the file or return immediately
2729 58885d79 Iustin Pop
    @type timeout: int or None
2730 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2731 58885d79 Iustin Pop
        (in blocking mode)
2732 58885d79 Iustin Pop

2733 a87b4824 Michael Hanselmann
    """
2734 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2735 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2736 a87b4824 Michael Hanselmann
2737 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2738 a87b4824 Michael Hanselmann
    """Unlocks the file.
2739 a87b4824 Michael Hanselmann

2740 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2741 58885d79 Iustin Pop
    operation::
2742 58885d79 Iustin Pop

2743 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2744 58885d79 Iustin Pop
      operations.
2745 58885d79 Iustin Pop

2746 58885d79 Iustin Pop
    @type blocking: boolean
2747 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2748 58885d79 Iustin Pop
        can lock the file or return immediately
2749 58885d79 Iustin Pop
    @type timeout: int or None
2750 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2751 58885d79 Iustin Pop
        (in blocking mode)
2752 a87b4824 Michael Hanselmann

2753 a87b4824 Michael Hanselmann
    """
2754 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2755 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2756 a87b4824 Michael Hanselmann
2757 a87b4824 Michael Hanselmann
2758 451575de Guido Trotter
def SignalHandled(signums):
2759 451575de Guido Trotter
  """Signal Handled decoration.
2760 451575de Guido Trotter

2761 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2762 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2763 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2764 451575de Guido Trotter
  objects as values.
2765 451575de Guido Trotter

2766 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2767 451575de Guido Trotter
  with different handlers.
2768 451575de Guido Trotter

2769 451575de Guido Trotter
  @type signums: list
2770 451575de Guido Trotter
  @param signums: signals to intercept
2771 451575de Guido Trotter

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

2798 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2799 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2800 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2801 58885d79 Iustin Pop
  signal was sent.
2802 58885d79 Iustin Pop

2803 58885d79 Iustin Pop
  @type signum: list
2804 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2805 58885d79 Iustin Pop
  @type called: boolean
2806 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2807 de499029 Michael Hanselmann

2808 de499029 Michael Hanselmann
  """
2809 de499029 Michael Hanselmann
  def __init__(self, signum):
2810 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2811 de499029 Michael Hanselmann

2812 58885d79 Iustin Pop
    @type signum: int or list of ints
2813 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2814 de499029 Michael Hanselmann

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

2843 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2844 58885d79 Iustin Pop

2845 de499029 Michael Hanselmann
    """
2846 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2847 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2848 de499029 Michael Hanselmann
      # If successful, remove from dict
2849 de499029 Michael Hanselmann
      del self._previous[signum]
2850 de499029 Michael Hanselmann
2851 de499029 Michael Hanselmann
  def Clear(self):
2852 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2853 de499029 Michael Hanselmann

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

2856 de499029 Michael Hanselmann
    """
2857 de499029 Michael Hanselmann
    self.called = False
2858 de499029 Michael Hanselmann
2859 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2860 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2861 de499029 Michael Hanselmann
    """Actual signal handling function.
2862 de499029 Michael Hanselmann

2863 de499029 Michael Hanselmann
    """
2864 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2865 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2866 de499029 Michael Hanselmann
    self.called = True
2867 a2d2e1a7 Iustin Pop
2868 a2d2e1a7 Iustin Pop
2869 a2d2e1a7 Iustin Pop
class FieldSet(object):
2870 a2d2e1a7 Iustin Pop
  """A simple field set.
2871 a2d2e1a7 Iustin Pop

2872 a2d2e1a7 Iustin Pop
  Among the features are:
2873 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2874 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2875 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2876 a2d2e1a7 Iustin Pop

2877 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2878 a2d2e1a7 Iustin Pop

2879 a2d2e1a7 Iustin Pop
  """
2880 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2881 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2882 a2d2e1a7 Iustin Pop
2883 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2884 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2885 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2886 a2d2e1a7 Iustin Pop
2887 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2888 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2889 a2d2e1a7 Iustin Pop

2890 a2d2e1a7 Iustin Pop
    @type field: str
2891 a2d2e1a7 Iustin Pop
    @param field: the string to match
2892 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2893 a2d2e1a7 Iustin Pop

2894 a2d2e1a7 Iustin Pop
    """
2895 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2896 a2d2e1a7 Iustin Pop
      return m
2897 6c881c52 Iustin Pop
    return None
2898 a2d2e1a7 Iustin Pop
2899 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2900 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2901 a2d2e1a7 Iustin Pop

2902 a2d2e1a7 Iustin Pop
    @type items: list
2903 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2904 a2d2e1a7 Iustin Pop
    @rtype: list
2905 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2906 a2d2e1a7 Iustin Pop

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