Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ e29e9550

History | View | Annotate | Download (75.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1471 7b4126b7 Iustin Pop
  """
1472 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1473 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1474 7b4126b7 Iustin Pop
    if elem > idx + base:
1475 7b4126b7 Iustin Pop
      # idx is not used
1476 7b4126b7 Iustin Pop
      return idx + base
1477 7b4126b7 Iustin Pop
  return None
1478 7b4126b7 Iustin Pop
1479 7b4126b7 Iustin Pop
1480 7260cfbe Iustin Pop
def all(seq, pred=bool): # pylint: disable-msg=W0622
1481 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1482 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilterfalse(pred, seq):
1483 78feb6fb Guido Trotter
    return False
1484 78feb6fb Guido Trotter
  return True
1485 78feb6fb Guido Trotter
1486 78feb6fb Guido Trotter
1487 7260cfbe Iustin Pop
def any(seq, pred=bool): # pylint: disable-msg=W0622
1488 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1489 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilter(pred, seq):
1490 78feb6fb Guido Trotter
    return True
1491 78feb6fb Guido Trotter
  return False
1492 f7414041 Michael Hanselmann
1493 f7414041 Michael Hanselmann
1494 2de64672 Iustin Pop
def partition(seq, pred=bool): # # pylint: disable-msg=W0622
1495 2de64672 Iustin Pop
  "Partition a list in two, based on the given predicate"
1496 2de64672 Iustin Pop
  return (list(itertools.ifilter(pred, seq)),
1497 2de64672 Iustin Pop
          list(itertools.ifilterfalse(pred, seq)))
1498 2de64672 Iustin Pop
1499 2de64672 Iustin Pop
1500 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1501 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1502 f7414041 Michael Hanselmann

1503 f7414041 Michael Hanselmann
  Element order is preserved.
1504 58885d79 Iustin Pop

1505 58885d79 Iustin Pop
  @type seq: sequence
1506 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1507 58885d79 Iustin Pop
  @rtype: list
1508 58885d79 Iustin Pop
  @return: list of unique elements from seq
1509 58885d79 Iustin Pop

1510 f7414041 Michael Hanselmann
  """
1511 f7414041 Michael Hanselmann
  seen = set()
1512 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1513 1862d460 Alexander Schreiber
1514 1862d460 Alexander Schreiber
1515 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
1516 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
1517 1862d460 Alexander Schreiber

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

1521 58885d79 Iustin Pop
  @type mac: str
1522 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1523 82187135 René Nussbaumer
  @rtype: str
1524 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
1525 82187135 René Nussbaumer

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

1528 1862d460 Alexander Schreiber
  """
1529 82187135 René Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1530 82187135 René Nussbaumer
  if not mac_check.match(mac):
1531 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1532 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
1533 82187135 René Nussbaumer
1534 82187135 René Nussbaumer
  return mac.lower()
1535 06009e27 Iustin Pop
1536 06009e27 Iustin Pop
1537 06009e27 Iustin Pop
def TestDelay(duration):
1538 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1539 06009e27 Iustin Pop

1540 58885d79 Iustin Pop
  @type duration: float
1541 58885d79 Iustin Pop
  @param duration: the sleep duration
1542 58885d79 Iustin Pop
  @rtype: boolean
1543 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1544 58885d79 Iustin Pop

1545 06009e27 Iustin Pop
  """
1546 06009e27 Iustin Pop
  if duration < 0:
1547 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1548 06009e27 Iustin Pop
  time.sleep(duration)
1549 38ea42a1 Iustin Pop
  return True, None
1550 8f765069 Iustin Pop
1551 8f765069 Iustin Pop
1552 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1553 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1554 8f765069 Iustin Pop

1555 7d88772a Iustin Pop
  @type fd: int
1556 7d88772a Iustin Pop
  @param fd: the file descriptor
1557 7d88772a Iustin Pop
  @type retries: int
1558 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1559 7d88772a Iustin Pop
      other error than EBADF
1560 7d88772a Iustin Pop

1561 7d88772a Iustin Pop
  """
1562 7d88772a Iustin Pop
  try:
1563 7d88772a Iustin Pop
    os.close(fd)
1564 7d88772a Iustin Pop
  except OSError, err:
1565 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1566 7d88772a Iustin Pop
      if retries > 0:
1567 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1568 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1569 7d88772a Iustin Pop
    # ignore this and go on
1570 7d88772a Iustin Pop
1571 7d88772a Iustin Pop
1572 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1573 7d88772a Iustin Pop
  """Close file descriptors.
1574 7d88772a Iustin Pop

1575 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1576 7d88772a Iustin Pop
  stdin/out/err).
1577 8f765069 Iustin Pop

1578 58885d79 Iustin Pop
  @type noclose_fds: list or None
1579 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1580 58885d79 Iustin Pop
      that should not be closed
1581 58885d79 Iustin Pop

1582 8f765069 Iustin Pop
  """
1583 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1584 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1585 8f765069 Iustin Pop
    try:
1586 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1587 8f765069 Iustin Pop
      if MAXFD < 0:
1588 8f765069 Iustin Pop
        MAXFD = 1024
1589 8f765069 Iustin Pop
    except OSError:
1590 8f765069 Iustin Pop
      MAXFD = 1024
1591 8f765069 Iustin Pop
  else:
1592 8f765069 Iustin Pop
    MAXFD = 1024
1593 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1594 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1595 7d88772a Iustin Pop
    maxfd = MAXFD
1596 7d88772a Iustin Pop
1597 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1598 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1599 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1600 7d88772a Iustin Pop
      continue
1601 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1602 7d88772a Iustin Pop
1603 7d88772a Iustin Pop
1604 7d88772a Iustin Pop
def Daemonize(logfile):
1605 7d88772a Iustin Pop
  """Daemonize the current process.
1606 7d88772a Iustin Pop

1607 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1608 7d88772a Iustin Pop
  runs it in the background as a daemon.
1609 7d88772a Iustin Pop

1610 7d88772a Iustin Pop
  @type logfile: str
1611 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1612 7d88772a Iustin Pop
  @rtype: int
1613 5fcc718f Iustin Pop
  @return: the value zero
1614 7d88772a Iustin Pop

1615 7d88772a Iustin Pop
  """
1616 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1617 7260cfbe Iustin Pop
  # yes, we really want os._exit
1618 7d88772a Iustin Pop
  UMASK = 077
1619 7d88772a Iustin Pop
  WORKDIR = "/"
1620 8f765069 Iustin Pop
1621 8f765069 Iustin Pop
  # this might fail
1622 8f765069 Iustin Pop
  pid = os.fork()
1623 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1624 8f765069 Iustin Pop
    os.setsid()
1625 8f765069 Iustin Pop
    # this might fail
1626 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1627 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1628 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1629 8f765069 Iustin Pop
      os.umask(UMASK)
1630 8f765069 Iustin Pop
    else:
1631 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1632 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1633 8f765069 Iustin Pop
  else:
1634 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1635 8f765069 Iustin Pop
1636 7d88772a Iustin Pop
  for fd in range(3):
1637 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1638 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1639 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1640 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1641 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1642 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1643 7d88772a Iustin Pop
  os.dup2(1, 2)
1644 8f765069 Iustin Pop
  return 0
1645 57c177af Iustin Pop
1646 57c177af Iustin Pop
1647 53beffbb Iustin Pop
def DaemonPidFileName(name):
1648 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1649 58885d79 Iustin Pop

1650 58885d79 Iustin Pop
  @type name: str
1651 58885d79 Iustin Pop
  @param name: the daemon name
1652 58885d79 Iustin Pop
  @rtype: str
1653 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1654 58885d79 Iustin Pop
      daemon name
1655 b330ac0b Guido Trotter

1656 b330ac0b Guido Trotter
  """
1657 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1658 b330ac0b Guido Trotter
1659 b330ac0b Guido Trotter
1660 2826b361 Guido Trotter
def EnsureDaemon(name):
1661 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1662 2826b361 Guido Trotter

1663 2826b361 Guido Trotter
  """
1664 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1665 2826b361 Guido Trotter
  if result.failed:
1666 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1667 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1668 2826b361 Guido Trotter
    return False
1669 2826b361 Guido Trotter
1670 2826b361 Guido Trotter
  return True
1671 2826b361 Guido Trotter
1672 2826b361 Guido Trotter
1673 b330ac0b Guido Trotter
def WritePidFile(name):
1674 b330ac0b Guido Trotter
  """Write the current process pidfile.
1675 b330ac0b Guido Trotter

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

1678 58885d79 Iustin Pop
  @type name: str
1679 58885d79 Iustin Pop
  @param name: the daemon name to use
1680 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1681 58885d79 Iustin Pop
      points to a live process
1682 b330ac0b Guido Trotter

1683 b330ac0b Guido Trotter
  """
1684 b330ac0b Guido Trotter
  pid = os.getpid()
1685 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1686 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1687 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1688 b330ac0b Guido Trotter
1689 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1690 b330ac0b Guido Trotter
1691 b330ac0b Guido Trotter
1692 b330ac0b Guido Trotter
def RemovePidFile(name):
1693 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1694 b330ac0b Guido Trotter

1695 b330ac0b Guido Trotter
  Any errors are ignored.
1696 b330ac0b Guido Trotter

1697 58885d79 Iustin Pop
  @type name: str
1698 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1699 58885d79 Iustin Pop

1700 b330ac0b Guido Trotter
  """
1701 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1702 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1703 b330ac0b Guido Trotter
  try:
1704 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1705 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1706 b330ac0b Guido Trotter
    pass
1707 b330ac0b Guido Trotter
1708 b330ac0b Guido Trotter
1709 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1710 ff5251bc Iustin Pop
                waitpid=False):
1711 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1712 b2a1f511 Iustin Pop

1713 b2a1f511 Iustin Pop
  @type pid: int
1714 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1715 38206f3c Iustin Pop
  @type signal_: int
1716 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1717 b2a1f511 Iustin Pop
  @type timeout: int
1718 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1719 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1720 b2a1f511 Iustin Pop
                  will be done
1721 ff5251bc Iustin Pop
  @type waitpid: boolean
1722 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1723 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1724 ff5251bc Iustin Pop
      would remain as zombie
1725 b2a1f511 Iustin Pop

1726 b2a1f511 Iustin Pop
  """
1727 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1728 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1729 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1730 ff5251bc Iustin Pop
    if wait:
1731 ff5251bc Iustin Pop
      try:
1732 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1733 ff5251bc Iustin Pop
      except OSError:
1734 ff5251bc Iustin Pop
        pass
1735 ff5251bc Iustin Pop
1736 b2a1f511 Iustin Pop
  if pid <= 0:
1737 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1738 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1739 b2a1f511 Iustin Pop
1740 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1741 b2a1f511 Iustin Pop
    return
1742 31892b4c Michael Hanselmann
1743 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1744 31892b4c Michael Hanselmann
1745 b2a1f511 Iustin Pop
  if timeout <= 0:
1746 b2a1f511 Iustin Pop
    return
1747 7167159a Michael Hanselmann
1748 31892b4c Michael Hanselmann
  def _CheckProcess():
1749 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1750 31892b4c Michael Hanselmann
      return
1751 31892b4c Michael Hanselmann
1752 7167159a Michael Hanselmann
    try:
1753 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1754 7167159a Michael Hanselmann
    except OSError:
1755 31892b4c Michael Hanselmann
      raise RetryAgain()
1756 31892b4c Michael Hanselmann
1757 31892b4c Michael Hanselmann
    if result_pid > 0:
1758 31892b4c Michael Hanselmann
      return
1759 31892b4c Michael Hanselmann
1760 31892b4c Michael Hanselmann
    raise RetryAgain()
1761 31892b4c Michael Hanselmann
1762 31892b4c Michael Hanselmann
  try:
1763 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1764 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1765 31892b4c Michael Hanselmann
  except RetryTimeout:
1766 31892b4c Michael Hanselmann
    pass
1767 7167159a Michael Hanselmann
1768 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1769 7167159a Michael Hanselmann
    # Kill process if it's still alive
1770 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1771 b2a1f511 Iustin Pop
1772 b2a1f511 Iustin Pop
1773 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1774 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1775 57c177af Iustin Pop

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

1779 58885d79 Iustin Pop
  @type name: str
1780 58885d79 Iustin Pop
  @param name: the name to look for
1781 58885d79 Iustin Pop
  @type search_path: str
1782 58885d79 Iustin Pop
  @param search_path: location to start at
1783 58885d79 Iustin Pop
  @type test: callable
1784 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1785 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1786 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1787 58885d79 Iustin Pop
  @rtype: str or None
1788 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1789 57c177af Iustin Pop

1790 57c177af Iustin Pop
  """
1791 f95c81bf Iustin Pop
  # validate the filename mask
1792 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1793 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1794 f95c81bf Iustin Pop
                     name)
1795 f95c81bf Iustin Pop
    return None
1796 f95c81bf Iustin Pop
1797 57c177af Iustin Pop
  for dir_name in search_path:
1798 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1799 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1800 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1801 f95c81bf Iustin Pop
    # basename
1802 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1803 57c177af Iustin Pop
      return item_name
1804 57c177af Iustin Pop
  return None
1805 8d1a2a64 Michael Hanselmann
1806 8d1a2a64 Michael Hanselmann
1807 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1808 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1809 8d1a2a64 Michael Hanselmann

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

1813 58885d79 Iustin Pop
  @type vglist: dict
1814 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1815 58885d79 Iustin Pop
  @type vgname: str
1816 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1817 58885d79 Iustin Pop
  @type minsize: int
1818 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1819 58885d79 Iustin Pop
  @rtype: None or str
1820 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1821 8d1a2a64 Michael Hanselmann

1822 8d1a2a64 Michael Hanselmann
  """
1823 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1824 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1825 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1826 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1827 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1828 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1829 8d1a2a64 Michael Hanselmann
  return None
1830 7996a135 Iustin Pop
1831 7996a135 Iustin Pop
1832 45bc5e4a Michael Hanselmann
def SplitTime(value):
1833 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1834 739be818 Michael Hanselmann

1835 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1836 45bc5e4a Michael Hanselmann
  @type value: int or float
1837 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1838 739be818 Michael Hanselmann

1839 739be818 Michael Hanselmann
  """
1840 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1841 45bc5e4a Michael Hanselmann
1842 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1843 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1844 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1845 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1846 45bc5e4a Michael Hanselmann
1847 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1848 739be818 Michael Hanselmann
1849 739be818 Michael Hanselmann
1850 739be818 Michael Hanselmann
def MergeTime(timetuple):
1851 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1852 739be818 Michael Hanselmann

1853 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1854 739be818 Michael Hanselmann
  @type timetuple: tuple
1855 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1856 739be818 Michael Hanselmann

1857 739be818 Michael Hanselmann
  """
1858 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1859 739be818 Michael Hanselmann
1860 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1861 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1862 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1863 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1864 739be818 Michael Hanselmann
1865 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1866 739be818 Michael Hanselmann
1867 739be818 Michael Hanselmann
1868 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1869 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1870 4a8b186a Michael Hanselmann

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

1875 cd50653c Guido Trotter
  @type daemon_name: string
1876 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1877 58885d79 Iustin Pop
  @rtype: int
1878 58885d79 Iustin Pop

1879 4a8b186a Michael Hanselmann
  """
1880 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1881 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1882 cd50653c Guido Trotter
1883 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1884 4a8b186a Michael Hanselmann
  try:
1885 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1886 4a8b186a Michael Hanselmann
  except socket.error:
1887 cd50653c Guido Trotter
    port = default_port
1888 4a8b186a Michael Hanselmann
1889 4a8b186a Michael Hanselmann
  return port
1890 4a8b186a Michael Hanselmann
1891 4a8b186a Michael Hanselmann
1892 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
1893 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
1894 82d9caef Iustin Pop
  """Configures the logging module.
1895 82d9caef Iustin Pop

1896 58885d79 Iustin Pop
  @type logfile: str
1897 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1898 ea34193f Iustin Pop
  @type debug: integer
1899 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
1900 58885d79 Iustin Pop
      only those at C{INFO} and above level
1901 58885d79 Iustin Pop
  @type stderr_logging: boolean
1902 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1903 58885d79 Iustin Pop
  @type program: str
1904 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1905 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1906 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1907 551b6283 Iustin Pop
  @type syslog: string
1908 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
1909 551b6283 Iustin Pop
      - if no, syslog is not used
1910 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
1911 551b6283 Iustin Pop
      - if only, only syslog is used
1912 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1913 551b6283 Iustin Pop
      syslog/stderr logging is disabled
1914 58885d79 Iustin Pop

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

1981 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1982 da961187 Guido Trotter

1983 da961187 Guido Trotter
  """
1984 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1985 82d9caef Iustin Pop
1986 016d04b3 Michael Hanselmann
1987 4bb678e9 Iustin Pop
def PathJoin(*args):
1988 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
1989 4bb678e9 Iustin Pop

1990 4bb678e9 Iustin Pop
  Requirements:
1991 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
1992 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
1993 4bb678e9 Iustin Pop
        since we check for normalization at the end
1994 4bb678e9 Iustin Pop

1995 4bb678e9 Iustin Pop
  @param args: the path components to be joined
1996 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
1997 4bb678e9 Iustin Pop

1998 4bb678e9 Iustin Pop
  """
1999 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2000 4bb678e9 Iustin Pop
  assert args
2001 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2002 4bb678e9 Iustin Pop
  root = args[0]
2003 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2004 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2005 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2006 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2007 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2008 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2009 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2010 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2011 4bb678e9 Iustin Pop
  if prefix != root:
2012 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2013 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2014 4bb678e9 Iustin Pop
  return result
2015 4bb678e9 Iustin Pop
2016 4bb678e9 Iustin Pop
2017 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2018 f65f63ef Iustin Pop
  """Return the last lines from a file.
2019 f65f63ef Iustin Pop

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

2024 f65f63ef Iustin Pop
  @param fname: the file name
2025 f65f63ef Iustin Pop
  @type lines: int
2026 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2027 f65f63ef Iustin Pop

2028 f65f63ef Iustin Pop
  """
2029 f65f63ef Iustin Pop
  fd = open(fname, "r")
2030 f65f63ef Iustin Pop
  try:
2031 f65f63ef Iustin Pop
    fd.seek(0, 2)
2032 f65f63ef Iustin Pop
    pos = fd.tell()
2033 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2034 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2035 f65f63ef Iustin Pop
    raw_data = fd.read()
2036 f65f63ef Iustin Pop
  finally:
2037 f65f63ef Iustin Pop
    fd.close()
2038 f65f63ef Iustin Pop
2039 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2040 f65f63ef Iustin Pop
  return rows[-lines:]
2041 f65f63ef Iustin Pop
2042 f65f63ef Iustin Pop
2043 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2044 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2045 27e46076 Michael Hanselmann

2046 27e46076 Michael Hanselmann
  @type value: string
2047 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2048 27e46076 Michael Hanselmann

2049 27e46076 Michael Hanselmann
  """
2050 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2051 27e46076 Michael Hanselmann
  if m:
2052 27e46076 Michael Hanselmann
    # We have an offset
2053 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2054 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2055 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2056 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2057 27e46076 Michael Hanselmann
  else:
2058 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2059 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2060 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2061 27e46076 Michael Hanselmann
    utcoffset = 0
2062 27e46076 Michael Hanselmann
2063 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2064 27e46076 Michael Hanselmann
2065 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2066 27e46076 Michael Hanselmann
2067 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2068 27e46076 Michael Hanselmann
2069 27e46076 Michael Hanselmann
2070 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2071 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2072 27e46076 Michael Hanselmann

2073 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2074 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2075 27e46076 Michael Hanselmann

2076 27e46076 Michael Hanselmann
  """
2077 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2078 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2079 27e46076 Michael Hanselmann
  try:
2080 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2081 27e46076 Michael Hanselmann
  except AttributeError:
2082 27e46076 Michael Hanselmann
    not_before = None
2083 27e46076 Michael Hanselmann
  else:
2084 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2085 27e46076 Michael Hanselmann
2086 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2087 27e46076 Michael Hanselmann
      not_before = None
2088 27e46076 Michael Hanselmann
    else:
2089 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2090 27e46076 Michael Hanselmann
2091 27e46076 Michael Hanselmann
  try:
2092 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2093 27e46076 Michael Hanselmann
  except AttributeError:
2094 27e46076 Michael Hanselmann
    not_after = None
2095 27e46076 Michael Hanselmann
  else:
2096 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2097 27e46076 Michael Hanselmann
2098 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2099 27e46076 Michael Hanselmann
      not_after = None
2100 27e46076 Michael Hanselmann
    else:
2101 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2102 27e46076 Michael Hanselmann
2103 27e46076 Michael Hanselmann
  return (not_before, not_after)
2104 27e46076 Michael Hanselmann
2105 27e46076 Michael Hanselmann
2106 26f15862 Iustin Pop
def SafeEncode(text):
2107 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2108 26f15862 Iustin Pop

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

2118 26f15862 Iustin Pop
  @type text: str or unicode
2119 26f15862 Iustin Pop
  @param text: input data
2120 26f15862 Iustin Pop
  @rtype: str
2121 26f15862 Iustin Pop
  @return: a safe version of text
2122 26f15862 Iustin Pop

2123 26f15862 Iustin Pop
  """
2124 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2125 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2126 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2127 d392fa34 Iustin Pop
  resu = ""
2128 d392fa34 Iustin Pop
  for char in text:
2129 d392fa34 Iustin Pop
    c = ord(char)
2130 d392fa34 Iustin Pop
    if char  == '\t':
2131 d392fa34 Iustin Pop
      resu += r'\t'
2132 d392fa34 Iustin Pop
    elif char == '\n':
2133 d392fa34 Iustin Pop
      resu += r'\n'
2134 d392fa34 Iustin Pop
    elif char == '\r':
2135 d392fa34 Iustin Pop
      resu += r'\'r'
2136 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2137 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2138 d392fa34 Iustin Pop
    else:
2139 d392fa34 Iustin Pop
      resu += char
2140 d392fa34 Iustin Pop
  return resu
2141 26f15862 Iustin Pop
2142 26f15862 Iustin Pop
2143 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2144 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2145 5b69bc7c Iustin Pop

2146 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2147 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2148 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2149 5b69bc7c Iustin Pop
  separator):
2150 5b69bc7c Iustin Pop
    - a plain , separates the elements
2151 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2152 5b69bc7c Iustin Pop
      backslash plus a separator comma
2153 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2154 5b69bc7c Iustin Pop
      non-separator comma
2155 5b69bc7c Iustin Pop

2156 5b69bc7c Iustin Pop
  @type text: string
2157 5b69bc7c Iustin Pop
  @param text: the string to split
2158 5b69bc7c Iustin Pop
  @type sep: string
2159 5b69bc7c Iustin Pop
  @param text: the separator
2160 5b69bc7c Iustin Pop
  @rtype: string
2161 5b69bc7c Iustin Pop
  @return: a list of strings
2162 5b69bc7c Iustin Pop

2163 5b69bc7c Iustin Pop
  """
2164 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2165 5b69bc7c Iustin Pop
  slist = text.split(sep)
2166 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2167 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2168 5b69bc7c Iustin Pop
  rlist = []
2169 5b69bc7c Iustin Pop
  while slist:
2170 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2171 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2172 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2173 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2174 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2175 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2176 5b69bc7c Iustin Pop
        # the next step
2177 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2178 5b69bc7c Iustin Pop
        continue
2179 5b69bc7c Iustin Pop
    rlist.append(e1)
2180 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2181 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2182 5b69bc7c Iustin Pop
  return rlist
2183 5b69bc7c Iustin Pop
2184 5b69bc7c Iustin Pop
2185 ab3e6da8 Iustin Pop
def CommaJoin(names):
2186 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2187 ab3e6da8 Iustin Pop

2188 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2189 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2190 ab3e6da8 Iustin Pop

2191 ab3e6da8 Iustin Pop
  """
2192 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2193 ab3e6da8 Iustin Pop
2194 ab3e6da8 Iustin Pop
2195 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2196 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2197 3f6a47a8 Michael Hanselmann

2198 3f6a47a8 Michael Hanselmann
  @type value: int
2199 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2200 3f6a47a8 Michael Hanselmann
  @rtype: int
2201 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2202 3f6a47a8 Michael Hanselmann

2203 3f6a47a8 Michael Hanselmann
  """
2204 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2205 3f6a47a8 Michael Hanselmann
2206 3f6a47a8 Michael Hanselmann
2207 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2208 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2209 3f6a47a8 Michael Hanselmann

2210 3f6a47a8 Michael Hanselmann
  @type path: string
2211 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2212 3f6a47a8 Michael Hanselmann
  @rtype: int
2213 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2214 3f6a47a8 Michael Hanselmann

2215 3f6a47a8 Michael Hanselmann
  """
2216 3f6a47a8 Michael Hanselmann
  size = 0
2217 3f6a47a8 Michael Hanselmann
2218 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2219 2a887df9 Michael Hanselmann
    for filename in files:
2220 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2221 3f6a47a8 Michael Hanselmann
      size += st.st_size
2222 3f6a47a8 Michael Hanselmann
2223 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2224 3f6a47a8 Michael Hanselmann
2225 3f6a47a8 Michael Hanselmann
2226 620a85fd Iustin Pop
def GetFilesystemStats(path):
2227 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2228 3f6a47a8 Michael Hanselmann

2229 3f6a47a8 Michael Hanselmann
  @type path: string
2230 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2231 3f6a47a8 Michael Hanselmann
  @rtype: int
2232 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2233 3f6a47a8 Michael Hanselmann

2234 3f6a47a8 Michael Hanselmann
  """
2235 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2236 3f6a47a8 Michael Hanselmann
2237 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2238 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2239 620a85fd Iustin Pop
  return (tsize, fsize)
2240 3f6a47a8 Michael Hanselmann
2241 3f6a47a8 Michael Hanselmann
2242 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2243 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2244 eb58f7bd Michael Hanselmann

2245 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2246 eb58f7bd Michael Hanselmann

2247 eb58f7bd Michael Hanselmann
  @type fn: callable
2248 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2249 bdefe5dd Michael Hanselmann
  @rtype: bool
2250 bdefe5dd Michael Hanselmann
  @return: Function's result
2251 eb58f7bd Michael Hanselmann

2252 eb58f7bd Michael Hanselmann
  """
2253 eb58f7bd Michael Hanselmann
  pid = os.fork()
2254 eb58f7bd Michael Hanselmann
  if pid == 0:
2255 eb58f7bd Michael Hanselmann
    # Child process
2256 eb58f7bd Michael Hanselmann
    try:
2257 82869978 Michael Hanselmann
      # In case the function uses temporary files
2258 82869978 Michael Hanselmann
      ResetTempfileModule()
2259 82869978 Michael Hanselmann
2260 eb58f7bd Michael Hanselmann
      # Call function
2261 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2262 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2263 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2264 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2265 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2266 eb58f7bd Michael Hanselmann
      result = 33
2267 eb58f7bd Michael Hanselmann
2268 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2269 eb58f7bd Michael Hanselmann
2270 eb58f7bd Michael Hanselmann
  # Parent process
2271 eb58f7bd Michael Hanselmann
2272 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2273 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2274 eb58f7bd Michael Hanselmann
2275 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2276 eb58f7bd Michael Hanselmann
    exitcode = None
2277 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2278 eb58f7bd Michael Hanselmann
  else:
2279 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2280 eb58f7bd Michael Hanselmann
    signum = None
2281 eb58f7bd Michael Hanselmann
2282 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2283 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2284 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2285 eb58f7bd Michael Hanselmann
2286 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2287 eb58f7bd Michael Hanselmann
2288 eb58f7bd Michael Hanselmann
2289 7996a135 Iustin Pop
def LockedMethod(fn):
2290 7996a135 Iustin Pop
  """Synchronized object access decorator.
2291 7996a135 Iustin Pop

2292 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2293 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2294 7996a135 Iustin Pop

2295 7996a135 Iustin Pop
  """
2296 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2297 e67bd559 Michael Hanselmann
    if debug_locks:
2298 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2299 e67bd559 Michael Hanselmann
2300 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2301 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2302 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2303 7996a135 Iustin Pop
    lock = self._lock
2304 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2305 7996a135 Iustin Pop
    lock.acquire()
2306 7996a135 Iustin Pop
    try:
2307 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2308 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2309 7996a135 Iustin Pop
    finally:
2310 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2311 7996a135 Iustin Pop
      lock.release()
2312 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2313 7996a135 Iustin Pop
    return result
2314 7996a135 Iustin Pop
  return wrapper
2315 eb0f0ce0 Michael Hanselmann
2316 eb0f0ce0 Michael Hanselmann
2317 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2318 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2319 eb0f0ce0 Michael Hanselmann

2320 58885d79 Iustin Pop
  @type fd: int
2321 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2322 58885d79 Iustin Pop

2323 eb0f0ce0 Michael Hanselmann
  """
2324 eb0f0ce0 Michael Hanselmann
  try:
2325 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2326 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2327 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2328 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2329 eb0f0ce0 Michael Hanselmann
    raise
2330 de499029 Michael Hanselmann
2331 de499029 Michael Hanselmann
2332 3b813dd2 Iustin Pop
def FormatTime(val):
2333 3b813dd2 Iustin Pop
  """Formats a time value.
2334 3b813dd2 Iustin Pop

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

2339 3b813dd2 Iustin Pop
  """
2340 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2341 3b813dd2 Iustin Pop
    return "N/A"
2342 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2343 3b813dd2 Iustin Pop
  # platforms
2344 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2345 3b813dd2 Iustin Pop
2346 3b813dd2 Iustin Pop
2347 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2348 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2349 05e50653 Michael Hanselmann

2350 5cbe43a5 Michael Hanselmann
  @type filename: string
2351 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2352 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2353 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2354 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2355 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2356 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2357 5cbe43a5 Michael Hanselmann

2358 05e50653 Michael Hanselmann
  """
2359 05e50653 Michael Hanselmann
  if now is None:
2360 05e50653 Michael Hanselmann
    now = time.time()
2361 05e50653 Michael Hanselmann
2362 05e50653 Michael Hanselmann
  try:
2363 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2364 05e50653 Michael Hanselmann
  except IOError, err:
2365 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2366 05e50653 Michael Hanselmann
      raise
2367 05e50653 Michael Hanselmann
    value = None
2368 05e50653 Michael Hanselmann
2369 05e50653 Michael Hanselmann
  if value is not None:
2370 05e50653 Michael Hanselmann
    try:
2371 05e50653 Michael Hanselmann
      value = int(value)
2372 05e50653 Michael Hanselmann
    except ValueError:
2373 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2374 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2375 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2376 05e50653 Michael Hanselmann
      value = None
2377 05e50653 Michael Hanselmann
2378 05e50653 Michael Hanselmann
    if value is not None:
2379 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2380 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2381 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2382 5cbe43a5 Michael Hanselmann
        value = None
2383 5cbe43a5 Michael Hanselmann
2384 5cbe43a5 Michael Hanselmann
      elif now > value:
2385 05e50653 Michael Hanselmann
        value = None
2386 05e50653 Michael Hanselmann
2387 05e50653 Michael Hanselmann
  return value
2388 05e50653 Michael Hanselmann
2389 05e50653 Michael Hanselmann
2390 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2391 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2392 de0ea66b Michael Hanselmann

2393 de0ea66b Michael Hanselmann
  """
2394 de0ea66b Michael Hanselmann
2395 de0ea66b Michael Hanselmann
2396 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2397 de0ea66b Michael Hanselmann
  """Retry again.
2398 de0ea66b Michael Hanselmann

2399 de0ea66b Michael Hanselmann
  """
2400 de0ea66b Michael Hanselmann
2401 de0ea66b Michael Hanselmann
2402 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2403 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2404 de0ea66b Michael Hanselmann

2405 de0ea66b Michael Hanselmann
  """
2406 de0ea66b Michael Hanselmann
  __slots__ = [
2407 de0ea66b Michael Hanselmann
    "_factor",
2408 de0ea66b Michael Hanselmann
    "_limit",
2409 de0ea66b Michael Hanselmann
    "_next",
2410 de0ea66b Michael Hanselmann
    "_start",
2411 de0ea66b Michael Hanselmann
    ]
2412 de0ea66b Michael Hanselmann
2413 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2414 de0ea66b Michael Hanselmann
    """Initializes this class.
2415 de0ea66b Michael Hanselmann

2416 de0ea66b Michael Hanselmann
    @type start: float
2417 de0ea66b Michael Hanselmann
    @param start: Initial delay
2418 de0ea66b Michael Hanselmann
    @type factor: float
2419 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2420 de0ea66b Michael Hanselmann
    @type limit: float or None
2421 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2422 de0ea66b Michael Hanselmann

2423 de0ea66b Michael Hanselmann
    """
2424 de0ea66b Michael Hanselmann
    assert start > 0.0
2425 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2426 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2427 de0ea66b Michael Hanselmann
2428 de0ea66b Michael Hanselmann
    self._start = start
2429 de0ea66b Michael Hanselmann
    self._factor = factor
2430 de0ea66b Michael Hanselmann
    self._limit = limit
2431 de0ea66b Michael Hanselmann
2432 de0ea66b Michael Hanselmann
    self._next = start
2433 de0ea66b Michael Hanselmann
2434 de0ea66b Michael Hanselmann
  def __call__(self):
2435 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2436 de0ea66b Michael Hanselmann

2437 de0ea66b Michael Hanselmann
    """
2438 de0ea66b Michael Hanselmann
    current = self._next
2439 de0ea66b Michael Hanselmann
2440 de0ea66b Michael Hanselmann
    # Update for next run
2441 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2442 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2443 de0ea66b Michael Hanselmann
2444 de0ea66b Michael Hanselmann
    return current
2445 de0ea66b Michael Hanselmann
2446 de0ea66b Michael Hanselmann
2447 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2448 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2449 de0ea66b Michael Hanselmann
2450 de0ea66b Michael Hanselmann
2451 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2452 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2453 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2454 de0ea66b Michael Hanselmann

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

2459 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2460 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2461 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2462 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2463 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2464 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2465 de0ea66b Michael Hanselmann

2466 de0ea66b Michael Hanselmann
  @type fn: callable
2467 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2468 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2469 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2470 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2471 de0ea66b Michael Hanselmann
  @type timeout: float
2472 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2473 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2474 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2475 de0ea66b Michael Hanselmann
  @return: Return value of function
2476 de0ea66b Michael Hanselmann

2477 de0ea66b Michael Hanselmann
  """
2478 de0ea66b Michael Hanselmann
  assert callable(fn)
2479 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2480 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2481 de0ea66b Michael Hanselmann
2482 de0ea66b Michael Hanselmann
  if args is None:
2483 de0ea66b Michael Hanselmann
    args = []
2484 de0ea66b Michael Hanselmann
2485 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2486 de0ea66b Michael Hanselmann
2487 de0ea66b Michael Hanselmann
  if callable(delay):
2488 de0ea66b Michael Hanselmann
    # External function to calculate delay
2489 de0ea66b Michael Hanselmann
    calc_delay = delay
2490 de0ea66b Michael Hanselmann
2491 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2492 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2493 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2494 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2495 de0ea66b Michael Hanselmann
2496 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2497 de0ea66b Michael Hanselmann
    # Always use the remaining time
2498 de0ea66b Michael Hanselmann
    calc_delay = None
2499 de0ea66b Michael Hanselmann
2500 de0ea66b Michael Hanselmann
  else:
2501 de0ea66b Michael Hanselmann
    # Static delay
2502 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2503 de0ea66b Michael Hanselmann
2504 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2505 de0ea66b Michael Hanselmann
2506 de0ea66b Michael Hanselmann
  while True:
2507 de0ea66b Michael Hanselmann
    try:
2508 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2509 de0ea66b Michael Hanselmann
      return fn(*args)
2510 de0ea66b Michael Hanselmann
    except RetryAgain:
2511 de0ea66b Michael Hanselmann
      pass
2512 de0ea66b Michael Hanselmann
2513 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2514 de0ea66b Michael Hanselmann
2515 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2516 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2517 de0ea66b Michael Hanselmann
2518 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2519 de0ea66b Michael Hanselmann
2520 de0ea66b Michael Hanselmann
    if calc_delay is None:
2521 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2522 de0ea66b Michael Hanselmann
    else:
2523 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2524 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2525 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2526 de0ea66b Michael Hanselmann
2527 de0ea66b Michael Hanselmann
2528 a87b4824 Michael Hanselmann
class FileLock(object):
2529 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2530 a87b4824 Michael Hanselmann

2531 a87b4824 Michael Hanselmann
  """
2532 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2533 58885d79 Iustin Pop
    """Constructor for FileLock.
2534 58885d79 Iustin Pop

2535 b4478d34 Michael Hanselmann
    @type fd: file
2536 b4478d34 Michael Hanselmann
    @param fd: File object
2537 58885d79 Iustin Pop
    @type filename: str
2538 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2539 58885d79 Iustin Pop

2540 58885d79 Iustin Pop
    """
2541 b4478d34 Michael Hanselmann
    self.fd = fd
2542 a87b4824 Michael Hanselmann
    self.filename = filename
2543 b4478d34 Michael Hanselmann
2544 b4478d34 Michael Hanselmann
  @classmethod
2545 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2546 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2547 b4478d34 Michael Hanselmann

2548 b4478d34 Michael Hanselmann
    @type filename: string
2549 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2550 b4478d34 Michael Hanselmann

2551 b4478d34 Michael Hanselmann
    """
2552 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2553 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2554 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2555 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2556 b4478d34 Michael Hanselmann
               filename)
2557 a87b4824 Michael Hanselmann
2558 a87b4824 Michael Hanselmann
  def __del__(self):
2559 a87b4824 Michael Hanselmann
    self.Close()
2560 a87b4824 Michael Hanselmann
2561 a87b4824 Michael Hanselmann
  def Close(self):
2562 58885d79 Iustin Pop
    """Close the file and release the lock.
2563 58885d79 Iustin Pop

2564 58885d79 Iustin Pop
    """
2565 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2566 a87b4824 Michael Hanselmann
      self.fd.close()
2567 a87b4824 Michael Hanselmann
      self.fd = None
2568 a87b4824 Michael Hanselmann
2569 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2570 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2571 aa74b828 Michael Hanselmann

2572 aa74b828 Michael Hanselmann
    @type flag: int
2573 58885d79 Iustin Pop
    @param flag: operation flag
2574 aa74b828 Michael Hanselmann
    @type blocking: bool
2575 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2576 aa74b828 Michael Hanselmann
    @type timeout: None or float
2577 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2578 aa74b828 Michael Hanselmann
                    non-blocking mode).
2579 aa74b828 Michael Hanselmann
    @type errmsg: string
2580 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2581 aa74b828 Michael Hanselmann

2582 aa74b828 Michael Hanselmann
    """
2583 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2584 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2585 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2586 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2587 a87b4824 Michael Hanselmann
2588 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2589 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2590 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2591 a87b4824 Michael Hanselmann
2592 cc4c9b91 Michael Hanselmann
    if timeout is None:
2593 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2594 cc4c9b91 Michael Hanselmann
    else:
2595 cc4c9b91 Michael Hanselmann
      try:
2596 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2597 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2598 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2599 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2600 aa74b828 Michael Hanselmann
2601 cc4c9b91 Michael Hanselmann
  @staticmethod
2602 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2603 cc4c9b91 Michael Hanselmann
    try:
2604 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2605 cc4c9b91 Michael Hanselmann
    except IOError, err:
2606 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2607 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2608 31892b4c Michael Hanselmann
2609 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2610 cc4c9b91 Michael Hanselmann
      raise
2611 aa74b828 Michael Hanselmann
2612 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2613 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2614 a87b4824 Michael Hanselmann

2615 58885d79 Iustin Pop
    @type blocking: boolean
2616 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2617 58885d79 Iustin Pop
        can lock the file or return immediately
2618 58885d79 Iustin Pop
    @type timeout: int or None
2619 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2620 58885d79 Iustin Pop
        (in blocking mode)
2621 58885d79 Iustin Pop

2622 a87b4824 Michael Hanselmann
    """
2623 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2624 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2625 a87b4824 Michael Hanselmann
2626 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2627 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2628 a87b4824 Michael Hanselmann

2629 58885d79 Iustin Pop
    @type blocking: boolean
2630 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2631 58885d79 Iustin Pop
        can lock the file or return immediately
2632 58885d79 Iustin Pop
    @type timeout: int or None
2633 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2634 58885d79 Iustin Pop
        (in blocking mode)
2635 58885d79 Iustin Pop

2636 a87b4824 Michael Hanselmann
    """
2637 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2638 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2639 a87b4824 Michael Hanselmann
2640 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2641 a87b4824 Michael Hanselmann
    """Unlocks the file.
2642 a87b4824 Michael Hanselmann

2643 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2644 58885d79 Iustin Pop
    operation::
2645 58885d79 Iustin Pop

2646 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2647 58885d79 Iustin Pop
      operations.
2648 58885d79 Iustin Pop

2649 58885d79 Iustin Pop
    @type blocking: boolean
2650 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2651 58885d79 Iustin Pop
        can lock the file or return immediately
2652 58885d79 Iustin Pop
    @type timeout: int or None
2653 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2654 58885d79 Iustin Pop
        (in blocking mode)
2655 a87b4824 Michael Hanselmann

2656 a87b4824 Michael Hanselmann
    """
2657 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2658 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2659 a87b4824 Michael Hanselmann
2660 a87b4824 Michael Hanselmann
2661 451575de Guido Trotter
def SignalHandled(signums):
2662 451575de Guido Trotter
  """Signal Handled decoration.
2663 451575de Guido Trotter

2664 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2665 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2666 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2667 451575de Guido Trotter
  objects as values.
2668 451575de Guido Trotter

2669 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2670 451575de Guido Trotter
  with different handlers.
2671 451575de Guido Trotter

2672 451575de Guido Trotter
  @type signums: list
2673 451575de Guido Trotter
  @param signums: signals to intercept
2674 451575de Guido Trotter

2675 451575de Guido Trotter
  """
2676 451575de Guido Trotter
  def wrap(fn):
2677 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2678 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2679 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2680 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2681 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2682 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2683 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2684 451575de Guido Trotter
      else:
2685 451575de Guido Trotter
        signal_handlers = {}
2686 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2687 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2688 451575de Guido Trotter
      try:
2689 451575de Guido Trotter
        for sig in signums:
2690 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2691 451575de Guido Trotter
        return fn(*args, **kwargs)
2692 451575de Guido Trotter
      finally:
2693 451575de Guido Trotter
        sighandler.Reset()
2694 451575de Guido Trotter
    return sig_function
2695 451575de Guido Trotter
  return wrap
2696 451575de Guido Trotter
2697 451575de Guido Trotter
2698 de499029 Michael Hanselmann
class SignalHandler(object):
2699 de499029 Michael Hanselmann
  """Generic signal handler class.
2700 de499029 Michael Hanselmann

2701 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2702 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2703 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2704 58885d79 Iustin Pop
  signal was sent.
2705 58885d79 Iustin Pop

2706 58885d79 Iustin Pop
  @type signum: list
2707 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2708 58885d79 Iustin Pop
  @type called: boolean
2709 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2710 de499029 Michael Hanselmann

2711 de499029 Michael Hanselmann
  """
2712 de499029 Michael Hanselmann
  def __init__(self, signum):
2713 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2714 de499029 Michael Hanselmann

2715 58885d79 Iustin Pop
    @type signum: int or list of ints
2716 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2717 de499029 Michael Hanselmann

2718 de499029 Michael Hanselmann
    """
2719 6c52849e Guido Trotter
    self.signum = set(signum)
2720 de499029 Michael Hanselmann
    self.called = False
2721 de499029 Michael Hanselmann
2722 de499029 Michael Hanselmann
    self._previous = {}
2723 de499029 Michael Hanselmann
    try:
2724 de499029 Michael Hanselmann
      for signum in self.signum:
2725 de499029 Michael Hanselmann
        # Setup handler
2726 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2727 de499029 Michael Hanselmann
        try:
2728 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2729 de499029 Michael Hanselmann
        except:
2730 de499029 Michael Hanselmann
          # Restore previous handler
2731 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2732 de499029 Michael Hanselmann
          raise
2733 de499029 Michael Hanselmann
    except:
2734 de499029 Michael Hanselmann
      # Reset all handlers
2735 de499029 Michael Hanselmann
      self.Reset()
2736 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2737 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2738 de499029 Michael Hanselmann
      raise
2739 de499029 Michael Hanselmann
2740 de499029 Michael Hanselmann
  def __del__(self):
2741 de499029 Michael Hanselmann
    self.Reset()
2742 de499029 Michael Hanselmann
2743 de499029 Michael Hanselmann
  def Reset(self):
2744 de499029 Michael Hanselmann
    """Restore previous handler.
2745 de499029 Michael Hanselmann

2746 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2747 58885d79 Iustin Pop

2748 de499029 Michael Hanselmann
    """
2749 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2750 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2751 de499029 Michael Hanselmann
      # If successful, remove from dict
2752 de499029 Michael Hanselmann
      del self._previous[signum]
2753 de499029 Michael Hanselmann
2754 de499029 Michael Hanselmann
  def Clear(self):
2755 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2756 de499029 Michael Hanselmann

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

2759 de499029 Michael Hanselmann
    """
2760 de499029 Michael Hanselmann
    self.called = False
2761 de499029 Michael Hanselmann
2762 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2763 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2764 de499029 Michael Hanselmann
    """Actual signal handling function.
2765 de499029 Michael Hanselmann

2766 de499029 Michael Hanselmann
    """
2767 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2768 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2769 de499029 Michael Hanselmann
    self.called = True
2770 a2d2e1a7 Iustin Pop
2771 a2d2e1a7 Iustin Pop
2772 a2d2e1a7 Iustin Pop
class FieldSet(object):
2773 a2d2e1a7 Iustin Pop
  """A simple field set.
2774 a2d2e1a7 Iustin Pop

2775 a2d2e1a7 Iustin Pop
  Among the features are:
2776 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2777 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2778 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2779 a2d2e1a7 Iustin Pop

2780 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2781 a2d2e1a7 Iustin Pop

2782 a2d2e1a7 Iustin Pop
  """
2783 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2784 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2785 a2d2e1a7 Iustin Pop
2786 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2787 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2788 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2789 a2d2e1a7 Iustin Pop
2790 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2791 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2792 a2d2e1a7 Iustin Pop

2793 a2d2e1a7 Iustin Pop
    @type field: str
2794 a2d2e1a7 Iustin Pop
    @param field: the string to match
2795 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2796 a2d2e1a7 Iustin Pop

2797 a2d2e1a7 Iustin Pop
    """
2798 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2799 a2d2e1a7 Iustin Pop
      return m
2800 6c881c52 Iustin Pop
    return None
2801 a2d2e1a7 Iustin Pop
2802 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2803 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2804 a2d2e1a7 Iustin Pop

2805 a2d2e1a7 Iustin Pop
    @type items: list
2806 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2807 a2d2e1a7 Iustin Pop
    @rtype: list
2808 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2809 a2d2e1a7 Iustin Pop

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