Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 682f7601

History | View | Annotate | Download (81.2 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 339be5a8 Michael Hanselmann
import collections
49 f93f2016 Michael Hanselmann
import struct
50 f93f2016 Michael Hanselmann
import IN
51 9c233417 Iustin Pop
52 9c233417 Iustin Pop
from cStringIO import StringIO
53 a8083063 Iustin Pop
54 7ffe8fba Carlos Valiente
try:
55 7ffe8fba Carlos Valiente
  from hashlib import sha1
56 7ffe8fba Carlos Valiente
except ImportError:
57 7ffe8fba Carlos Valiente
  import sha
58 7ffe8fba Carlos Valiente
  sha1 = sha.new
59 7ffe8fba Carlos Valiente
60 a8083063 Iustin Pop
from ganeti import errors
61 3aecd2c7 Iustin Pop
from ganeti import constants
62 a8083063 Iustin Pop
63 16abfbc2 Alexander Schreiber
64 a8083063 Iustin Pop
_locksheld = []
65 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
66 a8083063 Iustin Pop
67 e67bd559 Michael Hanselmann
debug_locks = False
68 58885d79 Iustin Pop
69 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
70 b74159ee Iustin Pop
no_fork = False
71 f362096f Iustin Pop
72 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
73 13998ef2 Michael Hanselmann
74 f93f2016 Michael Hanselmann
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
75 f93f2016 Michael Hanselmann
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
76 f93f2016 Michael Hanselmann
#
77 f93f2016 Michael Hanselmann
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
78 f93f2016 Michael Hanselmann
# pid_t to "int".
79 f93f2016 Michael Hanselmann
#
80 f93f2016 Michael Hanselmann
# IEEE Std 1003.1-2008:
81 f93f2016 Michael Hanselmann
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
82 f93f2016 Michael Hanselmann
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
83 f93f2016 Michael Hanselmann
_STRUCT_UCRED = "iII"
84 f93f2016 Michael Hanselmann
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
85 f93f2016 Michael Hanselmann
86 7c0d6283 Michael Hanselmann
87 a8083063 Iustin Pop
class RunResult(object):
88 58885d79 Iustin Pop
  """Holds the result of running external programs.
89 58885d79 Iustin Pop

90 58885d79 Iustin Pop
  @type exit_code: int
91 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
92 58885d79 Iustin Pop
      didn't exit())
93 58885d79 Iustin Pop
  @type signal: int or None
94 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
95 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
96 58885d79 Iustin Pop
  @type stdout: str
97 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
98 58885d79 Iustin Pop
  @type stderr: str
99 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
100 58885d79 Iustin Pop
  @type failed: boolean
101 58885d79 Iustin Pop
  @ivar failed: True in case the program was
102 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
103 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
104 a8083063 Iustin Pop

105 a8083063 Iustin Pop
  """
106 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
107 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
108 a8083063 Iustin Pop
109 a8083063 Iustin Pop
110 38206f3c Iustin Pop
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
111 a8083063 Iustin Pop
    self.cmd = cmd
112 a8083063 Iustin Pop
    self.exit_code = exit_code
113 38206f3c Iustin Pop
    self.signal = signal_
114 a8083063 Iustin Pop
    self.stdout = stdout
115 a8083063 Iustin Pop
    self.stderr = stderr
116 38206f3c Iustin Pop
    self.failed = (signal_ is not None or exit_code != 0)
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
    if self.signal is not None:
119 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
120 a8083063 Iustin Pop
    elif self.exit_code is not None:
121 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
122 a8083063 Iustin Pop
    else:
123 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
124 a8083063 Iustin Pop
125 bb698c1f Iustin Pop
    if self.failed:
126 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
127 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
128 f362096f Iustin Pop
129 a8083063 Iustin Pop
  def _GetOutput(self):
130 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
131 a8083063 Iustin Pop

132 a8083063 Iustin Pop
    """
133 a8083063 Iustin Pop
    return self.stdout + self.stderr
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
136 a8083063 Iustin Pop
137 a8083063 Iustin Pop
138 bf4daac9 Guido Trotter
def RunCmd(cmd, env=None, output=None, cwd='/', reset_env=False):
139 a8083063 Iustin Pop
  """Execute a (shell) command.
140 a8083063 Iustin Pop

141 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
142 a8083063 Iustin Pop
  closed.
143 a8083063 Iustin Pop

144 bf4daac9 Guido Trotter
  @type cmd: string or list
145 36117c2b Iustin Pop
  @param cmd: Command to run
146 2557ff82 Guido Trotter
  @type env: dict
147 58885d79 Iustin Pop
  @param env: Additional environment
148 36117c2b Iustin Pop
  @type output: str
149 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
150 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
151 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
152 8797df43 Iustin Pop
  @type cwd: string
153 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
154 8797df43 Iustin Pop
      directory for the command; the default will be /
155 bf4daac9 Guido Trotter
  @type reset_env: boolean
156 bf4daac9 Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
157 36117c2b Iustin Pop
  @rtype: L{RunResult}
158 58885d79 Iustin Pop
  @return: RunResult instance
159 5bbd3f7f Michael Hanselmann
  @raise errors.ProgrammerError: if we call this when forks are disabled
160 a8083063 Iustin Pop

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

209 36117c2b Iustin Pop
  @type  cmd: string or list
210 36117c2b Iustin Pop
  @param cmd: Command to run
211 36117c2b Iustin Pop
  @type env: dict
212 36117c2b Iustin Pop
  @param env: The environment to use
213 36117c2b Iustin Pop
  @type via_shell: bool
214 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
215 8797df43 Iustin Pop
  @type cwd: string
216 8797df43 Iustin Pop
  @param cwd: the working directory for the program
217 36117c2b Iustin Pop
  @rtype: tuple
218 36117c2b Iustin Pop
  @return: (out, err, status)
219 36117c2b Iustin Pop

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

278 36117c2b Iustin Pop
  @type  cmd: string or list
279 36117c2b Iustin Pop
  @param cmd: Command to run
280 36117c2b Iustin Pop
  @type env: dict
281 36117c2b Iustin Pop
  @param env: The environment to use
282 36117c2b Iustin Pop
  @type via_shell: bool
283 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
284 36117c2b Iustin Pop
  @type output: str
285 36117c2b Iustin Pop
  @param output: the filename in which to save the output
286 8797df43 Iustin Pop
  @type cwd: string
287 8797df43 Iustin Pop
  @param cwd: the working directory for the program
288 36117c2b Iustin Pop
  @rtype: int
289 36117c2b Iustin Pop
  @return: the exit status
290 36117c2b Iustin Pop

291 36117c2b Iustin Pop
  """
292 36117c2b Iustin Pop
  fh = open(output, "a")
293 36117c2b Iustin Pop
  try:
294 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
295 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
296 36117c2b Iustin Pop
                             stdout=fh,
297 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
298 8797df43 Iustin Pop
                             close_fds=True, env=env,
299 8797df43 Iustin Pop
                             cwd=cwd)
300 36117c2b Iustin Pop
301 36117c2b Iustin Pop
    child.stdin.close()
302 36117c2b Iustin Pop
    status = child.wait()
303 36117c2b Iustin Pop
  finally:
304 36117c2b Iustin Pop
    fh.close()
305 36117c2b Iustin Pop
  return status
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
308 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
309 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
310 6bb65e3a Guido Trotter

311 6bb65e3a Guido Trotter
  @type dir_name: string
312 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
313 6bb65e3a Guido Trotter
  @type env: dict
314 6bb65e3a Guido Trotter
  @param env: The environment to use
315 6bb65e3a Guido Trotter
  @type reset_env: boolean
316 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
317 6bb65e3a Guido Trotter
  @rtype: list of tuples
318 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
319 6bb65e3a Guido Trotter

320 6bb65e3a Guido Trotter
  """
321 6bb65e3a Guido Trotter
  rr = []
322 6bb65e3a Guido Trotter
323 6bb65e3a Guido Trotter
  try:
324 6bb65e3a Guido Trotter
    dir_contents = ListVisibleFiles(dir_name)
325 6bb65e3a Guido Trotter
  except OSError, err:
326 6bb65e3a Guido Trotter
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
327 6bb65e3a Guido Trotter
    return rr
328 6bb65e3a Guido Trotter
329 6bb65e3a Guido Trotter
  for relname in sorted(dir_contents):
330 c4feafe8 Iustin Pop
    fname = PathJoin(dir_name, relname)
331 6bb65e3a Guido Trotter
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
332 6bb65e3a Guido Trotter
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
333 6bb65e3a Guido Trotter
      rr.append((relname, constants.RUNPARTS_SKIP, None))
334 6bb65e3a Guido Trotter
    else:
335 6bb65e3a Guido Trotter
      try:
336 6bb65e3a Guido Trotter
        result = RunCmd([fname], env=env, reset_env=reset_env)
337 6bb65e3a Guido Trotter
      except Exception, err: # pylint: disable-msg=W0703
338 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
339 6bb65e3a Guido Trotter
      else:
340 6bb65e3a Guido Trotter
        rr.append((relname, constants.RUNPARTS_RUN, result))
341 6bb65e3a Guido Trotter
342 6bb65e3a Guido Trotter
  return rr
343 6bb65e3a Guido Trotter
344 6bb65e3a Guido Trotter
345 f93f2016 Michael Hanselmann
def GetSocketCredentials(sock):
346 f93f2016 Michael Hanselmann
  """Returns the credentials of the foreign process connected to a socket.
347 f93f2016 Michael Hanselmann

348 f93f2016 Michael Hanselmann
  @param sock: Unix socket
349 f93f2016 Michael Hanselmann
  @rtype: tuple; (number, number, number)
350 f93f2016 Michael Hanselmann
  @return: The PID, UID and GID of the connected foreign process.
351 f93f2016 Michael Hanselmann

352 f93f2016 Michael Hanselmann
  """
353 f93f2016 Michael Hanselmann
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
354 f93f2016 Michael Hanselmann
                             _STRUCT_UCRED_SIZE)
355 f93f2016 Michael Hanselmann
  return struct.unpack(_STRUCT_UCRED, peercred)
356 f93f2016 Michael Hanselmann
357 f93f2016 Michael Hanselmann
358 a8083063 Iustin Pop
def RemoveFile(filename):
359 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
360 a8083063 Iustin Pop

361 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
362 a8083063 Iustin Pop
  errors are passed.
363 a8083063 Iustin Pop

364 58885d79 Iustin Pop
  @type filename: str
365 58885d79 Iustin Pop
  @param filename: the file to be removed
366 58885d79 Iustin Pop

367 a8083063 Iustin Pop
  """
368 a8083063 Iustin Pop
  try:
369 a8083063 Iustin Pop
    os.unlink(filename)
370 a8083063 Iustin Pop
  except OSError, err:
371 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
372 a8083063 Iustin Pop
      raise
373 a8083063 Iustin Pop
374 a8083063 Iustin Pop
375 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
376 6e797216 Michael Hanselmann
  """Renames a file.
377 6e797216 Michael Hanselmann

378 6e797216 Michael Hanselmann
  @type old: string
379 6e797216 Michael Hanselmann
  @param old: Original path
380 6e797216 Michael Hanselmann
  @type new: string
381 6e797216 Michael Hanselmann
  @param new: New path
382 6e797216 Michael Hanselmann
  @type mkdir: bool
383 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
384 6e797216 Michael Hanselmann
  @type mkdir_mode: int
385 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
386 6e797216 Michael Hanselmann

387 6e797216 Michael Hanselmann
  """
388 6e797216 Michael Hanselmann
  try:
389 6e797216 Michael Hanselmann
    return os.rename(old, new)
390 6e797216 Michael Hanselmann
  except OSError, err:
391 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
392 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
393 6e797216 Michael Hanselmann
    # as efficient.
394 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
395 6e797216 Michael Hanselmann
      # Create directory and try again
396 cc2f004d Michael Hanselmann
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
397 a426508d Michael Hanselmann
398 6e797216 Michael Hanselmann
      return os.rename(old, new)
399 a426508d Michael Hanselmann
400 6e797216 Michael Hanselmann
    raise
401 6e797216 Michael Hanselmann
402 6e797216 Michael Hanselmann
403 76e5f8b5 Michael Hanselmann
def Makedirs(path, mode=0750):
404 76e5f8b5 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
405 76e5f8b5 Michael Hanselmann

406 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
407 76e5f8b5 Michael Hanselmann
  before Python 2.5.
408 76e5f8b5 Michael Hanselmann

409 76e5f8b5 Michael Hanselmann
  """
410 76e5f8b5 Michael Hanselmann
  try:
411 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
412 76e5f8b5 Michael Hanselmann
  except OSError, err:
413 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
414 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
415 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
416 76e5f8b5 Michael Hanselmann
      raise
417 76e5f8b5 Michael Hanselmann
418 76e5f8b5 Michael Hanselmann
419 055f822b Michael Hanselmann
def ResetTempfileModule():
420 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
421 055f822b Michael Hanselmann

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

428 055f822b Michael Hanselmann
  """
429 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
430 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
431 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
432 055f822b Michael Hanselmann
    try:
433 055f822b Michael Hanselmann
      # Reset random name generator
434 055f822b Michael Hanselmann
      tempfile._name_sequence = None
435 055f822b Michael Hanselmann
    finally:
436 055f822b Michael Hanselmann
      tempfile._once_lock.release()
437 055f822b Michael Hanselmann
  else:
438 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
439 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
440 055f822b Michael Hanselmann
441 055f822b Michael Hanselmann
442 a8083063 Iustin Pop
def _FingerprintFile(filename):
443 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
444 a8083063 Iustin Pop

445 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
446 a8083063 Iustin Pop
  instead.
447 a8083063 Iustin Pop

448 58885d79 Iustin Pop
  @type filename: str
449 58885d79 Iustin Pop
  @param filename: the filename to checksum
450 58885d79 Iustin Pop
  @rtype: str
451 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
452 58885d79 Iustin Pop
      of the file
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
  """
455 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
456 a8083063 Iustin Pop
    return None
457 a8083063 Iustin Pop
458 a8083063 Iustin Pop
  f = open(filename)
459 a8083063 Iustin Pop
460 7ffe8fba Carlos Valiente
  fp = sha1()
461 a8083063 Iustin Pop
  while True:
462 a8083063 Iustin Pop
    data = f.read(4096)
463 a8083063 Iustin Pop
    if not data:
464 a8083063 Iustin Pop
      break
465 a8083063 Iustin Pop
466 a8083063 Iustin Pop
    fp.update(data)
467 a8083063 Iustin Pop
468 a8083063 Iustin Pop
  return fp.hexdigest()
469 a8083063 Iustin Pop
470 a8083063 Iustin Pop
471 a8083063 Iustin Pop
def FingerprintFiles(files):
472 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
473 a8083063 Iustin Pop

474 58885d79 Iustin Pop
  @type files: list
475 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
476 58885d79 Iustin Pop
  @rtype: dict
477 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
478 58885d79 Iustin Pop
      existing files
479 a8083063 Iustin Pop

480 a8083063 Iustin Pop
  """
481 a8083063 Iustin Pop
  ret = {}
482 a8083063 Iustin Pop
483 a8083063 Iustin Pop
  for filename in files:
484 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
485 a8083063 Iustin Pop
    if cksum:
486 a8083063 Iustin Pop
      ret[filename] = cksum
487 a8083063 Iustin Pop
488 a8083063 Iustin Pop
  return ret
489 a8083063 Iustin Pop
490 a8083063 Iustin Pop
491 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
492 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
493 a5728081 Guido Trotter

494 a5728081 Guido Trotter
  @type target: dict
495 a5728081 Guido Trotter
  @param target: the dict to update
496 a5728081 Guido Trotter
  @type key_types: dict
497 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
498 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
499 a5728081 Guido Trotter
  @type allowed_values: list
500 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
501 a5728081 Guido Trotter

502 a5728081 Guido Trotter
  """
503 a5728081 Guido Trotter
  if allowed_values is None:
504 a5728081 Guido Trotter
    allowed_values = []
505 a5728081 Guido Trotter
506 8b46606c Guido Trotter
  if not isinstance(target, dict):
507 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
508 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
509 8b46606c Guido Trotter
510 a5728081 Guido Trotter
  for key in target:
511 a5728081 Guido Trotter
    if key not in key_types:
512 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
513 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
514 a5728081 Guido Trotter
515 a5728081 Guido Trotter
    if target[key] in allowed_values:
516 a5728081 Guido Trotter
      continue
517 a5728081 Guido Trotter
518 29921401 Iustin Pop
    ktype = key_types[key]
519 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
520 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
521 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
522 a5728081 Guido Trotter
523 29921401 Iustin Pop
    if ktype == constants.VTYPE_STRING:
524 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
525 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
526 a5728081 Guido Trotter
          target[key] = ''
527 a5728081 Guido Trotter
        else:
528 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
529 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
530 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
531 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
532 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
533 a5728081 Guido Trotter
          target[key] = False
534 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
535 a5728081 Guido Trotter
          target[key] = True
536 a5728081 Guido Trotter
        else:
537 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
538 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
539 a5728081 Guido Trotter
      elif target[key]:
540 a5728081 Guido Trotter
        target[key] = True
541 a5728081 Guido Trotter
      else:
542 a5728081 Guido Trotter
        target[key] = False
543 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
544 a5728081 Guido Trotter
      try:
545 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
546 a5728081 Guido Trotter
      except errors.UnitParseError, err:
547 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
548 a5728081 Guido Trotter
              (key, target[key], err)
549 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
550 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
551 a5728081 Guido Trotter
      try:
552 a5728081 Guido Trotter
        target[key] = int(target[key])
553 a5728081 Guido Trotter
      except (ValueError, TypeError):
554 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
555 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
556 a5728081 Guido Trotter
557 a5728081 Guido Trotter
558 a8083063 Iustin Pop
def IsProcessAlive(pid):
559 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
560 a8083063 Iustin Pop

561 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
562 44bf25ff Iustin Pop
      will be returned as alive
563 58885d79 Iustin Pop
  @type pid: int
564 58885d79 Iustin Pop
  @param pid: the process ID to check
565 58885d79 Iustin Pop
  @rtype: boolean
566 58885d79 Iustin Pop
  @return: True if the process exists
567 a8083063 Iustin Pop

568 a8083063 Iustin Pop
  """
569 5ef5ea45 Guido Trotter
  def _TryStat(name):
570 5ef5ea45 Guido Trotter
    try:
571 5ef5ea45 Guido Trotter
      os.stat(name)
572 5ef5ea45 Guido Trotter
      return True
573 5ef5ea45 Guido Trotter
    except EnvironmentError, err:
574 5ef5ea45 Guido Trotter
      if err.errno in (errno.ENOENT, errno.ENOTDIR):
575 5ef5ea45 Guido Trotter
        return False
576 5ef5ea45 Guido Trotter
      elif err.errno == errno.EINVAL:
577 5ef5ea45 Guido Trotter
        raise RetryAgain(err)
578 5ef5ea45 Guido Trotter
      raise
579 5ef5ea45 Guido Trotter
580 5ef5ea45 Guido Trotter
  assert isinstance(pid, int), "pid must be an integer"
581 d9f311d7 Iustin Pop
  if pid <= 0:
582 d9f311d7 Iustin Pop
    return False
583 d9f311d7 Iustin Pop
584 5ef5ea45 Guido Trotter
  proc_entry = "/proc/%d/status" % pid
585 5ef5ea45 Guido Trotter
  # /proc in a multiprocessor environment can have strange behaviors.
586 5ef5ea45 Guido Trotter
  # Retry the os.stat a few times until we get a good result.
587 a8083063 Iustin Pop
  try:
588 5ef5ea45 Guido Trotter
    return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5, args=[proc_entry])
589 5ef5ea45 Guido Trotter
  except RetryTimeout, err:
590 5ef5ea45 Guido Trotter
    err.RaiseInner()
591 a8083063 Iustin Pop
592 a8083063 Iustin Pop
593 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
594 58885d79 Iustin Pop
  """Read a pid from a file.
595 fee80e90 Guido Trotter

596 58885d79 Iustin Pop
  @type  pidfile: string
597 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
598 58885d79 Iustin Pop
  @rtype: int
599 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
600 d9f311d7 Iustin Pop
           otherwise 0
601 fee80e90 Guido Trotter

602 fee80e90 Guido Trotter
  """
603 fee80e90 Guido Trotter
  try:
604 682f7601 Guido Trotter
    raw_data = ReadOneLineFile(pidfile)
605 d9f311d7 Iustin Pop
  except EnvironmentError, err:
606 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
607 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
608 d9f311d7 Iustin Pop
    return 0
609 fee80e90 Guido Trotter
610 fee80e90 Guido Trotter
  try:
611 13998ef2 Michael Hanselmann
    pid = int(raw_data)
612 691744c4 Iustin Pop
  except (TypeError, ValueError), err:
613 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
614 d9f311d7 Iustin Pop
    return 0
615 fee80e90 Guido Trotter
616 d9f311d7 Iustin Pop
  return pid
617 fee80e90 Guido Trotter
618 fee80e90 Guido Trotter
619 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
620 a8083063 Iustin Pop
  """Try to match a name against a list.
621 a8083063 Iustin Pop

622 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
623 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
624 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
625 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
626 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
627 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
628 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
629 a8083063 Iustin Pop

630 58885d79 Iustin Pop
  @type key: str
631 58885d79 Iustin Pop
  @param key: the name to be searched
632 58885d79 Iustin Pop
  @type name_list: list
633 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
634 256eb94b Guido Trotter
  @type case_sensitive: boolean
635 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
636 a8083063 Iustin Pop

637 58885d79 Iustin Pop
  @rtype: None or str
638 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
639 58885d79 Iustin Pop
      otherwise the element from the list which matches
640 a8083063 Iustin Pop

641 a8083063 Iustin Pop
  """
642 3a541d90 Iustin Pop
  if key in name_list:
643 3a541d90 Iustin Pop
    return key
644 256eb94b Guido Trotter
645 256eb94b Guido Trotter
  re_flags = 0
646 256eb94b Guido Trotter
  if not case_sensitive:
647 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
648 099c52ad Iustin Pop
    key = key.upper()
649 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
650 256eb94b Guido Trotter
  names_filtered = []
651 256eb94b Guido Trotter
  string_matches = []
652 256eb94b Guido Trotter
  for name in name_list:
653 256eb94b Guido Trotter
    if mo.match(name) is not None:
654 256eb94b Guido Trotter
      names_filtered.append(name)
655 099c52ad Iustin Pop
      if not case_sensitive and key == name.upper():
656 256eb94b Guido Trotter
        string_matches.append(name)
657 256eb94b Guido Trotter
658 256eb94b Guido Trotter
  if len(string_matches) == 1:
659 256eb94b Guido Trotter
    return string_matches[0]
660 256eb94b Guido Trotter
  if len(names_filtered) == 1:
661 256eb94b Guido Trotter
    return names_filtered[0]
662 256eb94b Guido Trotter
  return None
663 a8083063 Iustin Pop
664 a8083063 Iustin Pop
665 bcf043c9 Iustin Pop
class HostInfo:
666 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
667 bcf043c9 Iustin Pop

668 bcf043c9 Iustin Pop
  """
669 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
670 26288e68 Iustin Pop
671 89e1fc26 Iustin Pop
  def __init__(self, name=None):
672 bcf043c9 Iustin Pop
    """Initialize the host name object.
673 bcf043c9 Iustin Pop

674 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
675 89e1fc26 Iustin Pop
    name.
676 bcf043c9 Iustin Pop

677 bcf043c9 Iustin Pop
    """
678 89e1fc26 Iustin Pop
    if name is None:
679 89e1fc26 Iustin Pop
      name = self.SysName()
680 89e1fc26 Iustin Pop
681 89e1fc26 Iustin Pop
    self.query = name
682 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
683 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
684 bcf043c9 Iustin Pop
685 c8a0948f Michael Hanselmann
  def ShortName(self):
686 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
687 c8a0948f Michael Hanselmann

688 c8a0948f Michael Hanselmann
    """
689 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
690 c8a0948f Michael Hanselmann
691 89e1fc26 Iustin Pop
  @staticmethod
692 89e1fc26 Iustin Pop
  def SysName():
693 89e1fc26 Iustin Pop
    """Return the current system's name.
694 bcf043c9 Iustin Pop

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

697 89e1fc26 Iustin Pop
    """
698 89e1fc26 Iustin Pop
    return socket.gethostname()
699 a8083063 Iustin Pop
700 89e1fc26 Iustin Pop
  @staticmethod
701 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
702 89e1fc26 Iustin Pop
    """Look up hostname
703 a8083063 Iustin Pop

704 58885d79 Iustin Pop
    @type hostname: str
705 58885d79 Iustin Pop
    @param hostname: hostname to look up
706 89e1fc26 Iustin Pop

707 58885d79 Iustin Pop
    @rtype: tuple
708 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
709 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
710 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
711 89e1fc26 Iustin Pop

712 89e1fc26 Iustin Pop
    """
713 89e1fc26 Iustin Pop
    try:
714 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
715 89e1fc26 Iustin Pop
    except socket.gaierror, err:
716 89e1fc26 Iustin Pop
      # hostname not found in DNS
717 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
718 a8083063 Iustin Pop
719 89e1fc26 Iustin Pop
    return result
720 a8083063 Iustin Pop
721 26288e68 Iustin Pop
  @classmethod
722 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
723 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
724 26288e68 Iustin Pop

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

729 26288e68 Iustin Pop
    """
730 26288e68 Iustin Pop
    hostname = hostname.lower()
731 26288e68 Iustin Pop
    if (not cls._VALID_NAME_RE.match(hostname) or
732 26288e68 Iustin Pop
        # double-dots, meaning empty label
733 26288e68 Iustin Pop
        ".." in hostname or
734 26288e68 Iustin Pop
        # empty initial label
735 26288e68 Iustin Pop
        hostname.startswith(".")):
736 26288e68 Iustin Pop
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
737 26288e68 Iustin Pop
                                 errors.ECODE_INVAL)
738 26288e68 Iustin Pop
    if hostname.endswith("."):
739 26288e68 Iustin Pop
      hostname = hostname.rstrip(".")
740 26288e68 Iustin Pop
    return hostname
741 26288e68 Iustin Pop
742 a8083063 Iustin Pop
743 104f4ca1 Iustin Pop
def GetHostInfo(name=None):
744 104f4ca1 Iustin Pop
  """Lookup host name and raise an OpPrereqError for failures"""
745 104f4ca1 Iustin Pop
746 104f4ca1 Iustin Pop
  try:
747 104f4ca1 Iustin Pop
    return HostInfo(name)
748 104f4ca1 Iustin Pop
  except errors.ResolverError, err:
749 104f4ca1 Iustin Pop
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
750 104f4ca1 Iustin Pop
                               (err[0], err[2]), errors.ECODE_RESOLVER)
751 104f4ca1 Iustin Pop
752 104f4ca1 Iustin Pop
753 a8083063 Iustin Pop
def ListVolumeGroups():
754 a8083063 Iustin Pop
  """List volume groups and their size
755 a8083063 Iustin Pop

756 58885d79 Iustin Pop
  @rtype: dict
757 58885d79 Iustin Pop
  @return:
758 58885d79 Iustin Pop
       Dictionary with keys volume name and values
759 58885d79 Iustin Pop
       the size of the volume
760 a8083063 Iustin Pop

761 a8083063 Iustin Pop
  """
762 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
763 a8083063 Iustin Pop
  result = RunCmd(command)
764 a8083063 Iustin Pop
  retval = {}
765 a8083063 Iustin Pop
  if result.failed:
766 a8083063 Iustin Pop
    return retval
767 a8083063 Iustin Pop
768 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
769 a8083063 Iustin Pop
    try:
770 a8083063 Iustin Pop
      name, size = line.split()
771 a8083063 Iustin Pop
      size = int(float(size))
772 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
773 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
774 a8083063 Iustin Pop
      continue
775 a8083063 Iustin Pop
776 a8083063 Iustin Pop
    retval[name] = size
777 a8083063 Iustin Pop
778 a8083063 Iustin Pop
  return retval
779 a8083063 Iustin Pop
780 a8083063 Iustin Pop
781 a8083063 Iustin Pop
def BridgeExists(bridge):
782 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
783 a8083063 Iustin Pop

784 58885d79 Iustin Pop
  @type bridge: str
785 58885d79 Iustin Pop
  @param bridge: the bridge name to check
786 58885d79 Iustin Pop
  @rtype: boolean
787 58885d79 Iustin Pop
  @return: True if it does
788 a8083063 Iustin Pop

789 a8083063 Iustin Pop
  """
790 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
791 a8083063 Iustin Pop
792 a8083063 Iustin Pop
793 a8083063 Iustin Pop
def NiceSort(name_list):
794 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
795 a8083063 Iustin Pop

796 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
797 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
798 58885d79 Iustin Pop
  'a11']}.
799 a8083063 Iustin Pop

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

804 58885d79 Iustin Pop
  @type name_list: list
805 58885d79 Iustin Pop
  @param name_list: the names to be sorted
806 58885d79 Iustin Pop
  @rtype: list
807 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
808 a8083063 Iustin Pop

809 a8083063 Iustin Pop
  """
810 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
811 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
812 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
813 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
814 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
815 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
816 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
817 a8083063 Iustin Pop
  def _TryInt(val):
818 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
819 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
820 a8083063 Iustin Pop
      return val
821 a8083063 Iustin Pop
    rval = int(val)
822 a8083063 Iustin Pop
    return rval
823 a8083063 Iustin Pop
824 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
825 a8083063 Iustin Pop
             for name in name_list]
826 a8083063 Iustin Pop
  to_sort.sort()
827 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
828 a8083063 Iustin Pop
829 a8083063 Iustin Pop
830 a8083063 Iustin Pop
def TryConvert(fn, val):
831 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
832 a8083063 Iustin Pop

833 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
834 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
835 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
836 58885d79 Iustin Pop
  exceptions are propagated to the caller.
837 58885d79 Iustin Pop

838 58885d79 Iustin Pop
  @type fn: callable
839 58885d79 Iustin Pop
  @param fn: function to apply to the value
840 58885d79 Iustin Pop
  @param val: the value to be converted
841 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
842 58885d79 Iustin Pop
      otherwise the original value.
843 a8083063 Iustin Pop

844 a8083063 Iustin Pop
  """
845 a8083063 Iustin Pop
  try:
846 a8083063 Iustin Pop
    nv = fn(val)
847 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
848 a8083063 Iustin Pop
    nv = val
849 a8083063 Iustin Pop
  return nv
850 a8083063 Iustin Pop
851 a8083063 Iustin Pop
852 a8083063 Iustin Pop
def IsValidIP(ip):
853 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
854 a8083063 Iustin Pop

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

858 58885d79 Iustin Pop
  @type ip: str
859 58885d79 Iustin Pop
  @param ip: the address to be checked
860 58885d79 Iustin Pop
  @rtype: a regular expression match object
861 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
862 58885d79 Iustin Pop
      address is not valid
863 a8083063 Iustin Pop

864 a8083063 Iustin Pop
  """
865 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
866 58885d79 Iustin Pop
  #TODO: convert and return only boolean
867 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
868 a8083063 Iustin Pop
869 a8083063 Iustin Pop
870 a8083063 Iustin Pop
def IsValidShellParam(word):
871 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
874 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
875 a8083063 Iustin Pop
  the actual command.
876 a8083063 Iustin Pop

877 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
878 a8083063 Iustin Pop
  side.
879 a8083063 Iustin Pop

880 58885d79 Iustin Pop
  @type word: str
881 58885d79 Iustin Pop
  @param word: the word to check
882 58885d79 Iustin Pop
  @rtype: boolean
883 58885d79 Iustin Pop
  @return: True if the word is 'safe'
884 58885d79 Iustin Pop

885 a8083063 Iustin Pop
  """
886 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
887 a8083063 Iustin Pop
888 a8083063 Iustin Pop
889 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
890 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
891 a8083063 Iustin Pop

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

897 58885d79 Iustin Pop
  @type template: str
898 58885d79 Iustin Pop
  @param template: the string holding the template for the
899 58885d79 Iustin Pop
      string formatting
900 58885d79 Iustin Pop
  @rtype: str
901 58885d79 Iustin Pop
  @return: the expanded command line
902 58885d79 Iustin Pop

903 a8083063 Iustin Pop
  """
904 a8083063 Iustin Pop
  for word in args:
905 a8083063 Iustin Pop
    if not IsValidShellParam(word):
906 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
907 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
908 a8083063 Iustin Pop
  return template % args
909 a8083063 Iustin Pop
910 a8083063 Iustin Pop
911 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
912 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
913 a8083063 Iustin Pop

914 58885d79 Iustin Pop
  @type value: int
915 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
916 9fbfbb7b Iustin Pop
  @type units: char
917 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
918 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
919 9fbfbb7b Iustin Pop
      - 'm' for MiBs
920 9fbfbb7b Iustin Pop
      - 'g' for GiBs
921 9fbfbb7b Iustin Pop
      - 't' for TiBs
922 58885d79 Iustin Pop
  @rtype: str
923 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
924 a8083063 Iustin Pop

925 a8083063 Iustin Pop
  """
926 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
927 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
928 a8083063 Iustin Pop
929 9fbfbb7b Iustin Pop
  suffix = ''
930 9fbfbb7b Iustin Pop
931 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
932 9fbfbb7b Iustin Pop
    if units == 'h':
933 9fbfbb7b Iustin Pop
      suffix = 'M'
934 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
935 9fbfbb7b Iustin Pop
936 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
937 9fbfbb7b Iustin Pop
    if units == 'h':
938 9fbfbb7b Iustin Pop
      suffix = 'G'
939 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
940 a8083063 Iustin Pop
941 a8083063 Iustin Pop
  else:
942 9fbfbb7b Iustin Pop
    if units == 'h':
943 9fbfbb7b Iustin Pop
      suffix = 'T'
944 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
945 a8083063 Iustin Pop
946 a8083063 Iustin Pop
947 a8083063 Iustin Pop
def ParseUnit(input_string):
948 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
949 a8083063 Iustin Pop

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

954 a8083063 Iustin Pop
  """
955 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
956 a8083063 Iustin Pop
  if not m:
957 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
958 a8083063 Iustin Pop
959 a8083063 Iustin Pop
  value = float(m.groups()[0])
960 a8083063 Iustin Pop
961 a8083063 Iustin Pop
  unit = m.groups()[1]
962 a8083063 Iustin Pop
  if unit:
963 a8083063 Iustin Pop
    lcunit = unit.lower()
964 a8083063 Iustin Pop
  else:
965 a8083063 Iustin Pop
    lcunit = 'm'
966 a8083063 Iustin Pop
967 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
968 a8083063 Iustin Pop
    # Value already in MiB
969 a8083063 Iustin Pop
    pass
970 a8083063 Iustin Pop
971 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
972 a8083063 Iustin Pop
    value *= 1024
973 a8083063 Iustin Pop
974 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
975 a8083063 Iustin Pop
    value *= 1024 * 1024
976 a8083063 Iustin Pop
977 a8083063 Iustin Pop
  else:
978 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
979 a8083063 Iustin Pop
980 a8083063 Iustin Pop
  # Make sure we round up
981 a8083063 Iustin Pop
  if int(value) < value:
982 a8083063 Iustin Pop
    value += 1
983 a8083063 Iustin Pop
984 a8083063 Iustin Pop
  # Round up to the next multiple of 4
985 a8083063 Iustin Pop
  value = int(value)
986 a8083063 Iustin Pop
  if value % 4:
987 a8083063 Iustin Pop
    value += 4 - value % 4
988 a8083063 Iustin Pop
989 a8083063 Iustin Pop
  return value
990 a8083063 Iustin Pop
991 a8083063 Iustin Pop
992 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
993 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
994 a8083063 Iustin Pop

995 58885d79 Iustin Pop
  @type file_name: str
996 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
997 58885d79 Iustin Pop
  @type key: str
998 58885d79 Iustin Pop
  @param key: string containing key
999 58885d79 Iustin Pop

1000 a8083063 Iustin Pop
  """
1001 a8083063 Iustin Pop
  key_fields = key.split()
1002 a8083063 Iustin Pop
1003 a8083063 Iustin Pop
  f = open(file_name, 'a+')
1004 a8083063 Iustin Pop
  try:
1005 a8083063 Iustin Pop
    nl = True
1006 a8083063 Iustin Pop
    for line in f:
1007 a8083063 Iustin Pop
      # Ignore whitespace changes
1008 a8083063 Iustin Pop
      if line.split() == key_fields:
1009 a8083063 Iustin Pop
        break
1010 a8083063 Iustin Pop
      nl = line.endswith('\n')
1011 a8083063 Iustin Pop
    else:
1012 a8083063 Iustin Pop
      if not nl:
1013 a8083063 Iustin Pop
        f.write("\n")
1014 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1015 a8083063 Iustin Pop
      f.write("\n")
1016 a8083063 Iustin Pop
      f.flush()
1017 a8083063 Iustin Pop
  finally:
1018 a8083063 Iustin Pop
    f.close()
1019 a8083063 Iustin Pop
1020 a8083063 Iustin Pop
1021 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1022 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1023 a8083063 Iustin Pop

1024 58885d79 Iustin Pop
  @type file_name: str
1025 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1026 58885d79 Iustin Pop
  @type key: str
1027 58885d79 Iustin Pop
  @param key: string containing key
1028 58885d79 Iustin Pop

1029 a8083063 Iustin Pop
  """
1030 a8083063 Iustin Pop
  key_fields = key.split()
1031 a8083063 Iustin Pop
1032 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1033 a8083063 Iustin Pop
  try:
1034 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
1035 a8083063 Iustin Pop
    try:
1036 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
1037 59f82e3f Michael Hanselmann
      try:
1038 59f82e3f Michael Hanselmann
        for line in f:
1039 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
1040 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
1041 59f82e3f Michael Hanselmann
            out.write(line)
1042 899d2a81 Michael Hanselmann
1043 899d2a81 Michael Hanselmann
        out.flush()
1044 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
1045 899d2a81 Michael Hanselmann
      finally:
1046 899d2a81 Michael Hanselmann
        f.close()
1047 899d2a81 Michael Hanselmann
    finally:
1048 899d2a81 Michael Hanselmann
      out.close()
1049 899d2a81 Michael Hanselmann
  except:
1050 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
1051 899d2a81 Michael Hanselmann
    raise
1052 899d2a81 Michael Hanselmann
1053 899d2a81 Michael Hanselmann
1054 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1055 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
1056 899d2a81 Michael Hanselmann

1057 58885d79 Iustin Pop
  @type file_name: str
1058 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1059 58885d79 Iustin Pop
  @type ip: str
1060 58885d79 Iustin Pop
  @param ip: the IP address
1061 58885d79 Iustin Pop
  @type hostname: str
1062 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1063 58885d79 Iustin Pop
  @type aliases: list
1064 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1065 58885d79 Iustin Pop

1066 899d2a81 Michael Hanselmann
  """
1067 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1068 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
1069 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
1070 7fbb1f65 Michael Hanselmann
1071 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1072 899d2a81 Michael Hanselmann
  try:
1073 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
1074 9440aeab Michael Hanselmann
    try:
1075 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
1076 9440aeab Michael Hanselmann
      try:
1077 9440aeab Michael Hanselmann
        for line in f:
1078 9440aeab Michael Hanselmann
          fields = line.split()
1079 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1080 9440aeab Michael Hanselmann
            continue
1081 9440aeab Michael Hanselmann
          out.write(line)
1082 9440aeab Michael Hanselmann
1083 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
1084 9440aeab Michael Hanselmann
        if aliases:
1085 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
1086 9440aeab Michael Hanselmann
        out.write('\n')
1087 9440aeab Michael Hanselmann
1088 9440aeab Michael Hanselmann
        out.flush()
1089 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1090 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1091 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
1092 9440aeab Michael Hanselmann
      finally:
1093 9440aeab Michael Hanselmann
        f.close()
1094 9440aeab Michael Hanselmann
    finally:
1095 9440aeab Michael Hanselmann
      out.close()
1096 9440aeab Michael Hanselmann
  except:
1097 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
1098 9440aeab Michael Hanselmann
    raise
1099 899d2a81 Michael Hanselmann
1100 899d2a81 Michael Hanselmann
1101 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
1102 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
1103 d9c02ca6 Michael Hanselmann

1104 58885d79 Iustin Pop
  @type hostname: str
1105 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1106 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1107 58885d79 Iustin Pop

1108 d9c02ca6 Michael Hanselmann
  """
1109 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1110 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1111 d9c02ca6 Michael Hanselmann
1112 d9c02ca6 Michael Hanselmann
1113 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1114 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1115 899d2a81 Michael Hanselmann

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

1118 58885d79 Iustin Pop
  @type file_name: str
1119 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1120 58885d79 Iustin Pop
  @type hostname: str
1121 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1122 58885d79 Iustin Pop

1123 899d2a81 Michael Hanselmann
  """
1124 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1125 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1126 899d2a81 Michael Hanselmann
  try:
1127 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
1128 899d2a81 Michael Hanselmann
    try:
1129 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
1130 899d2a81 Michael Hanselmann
      try:
1131 899d2a81 Michael Hanselmann
        for line in f:
1132 899d2a81 Michael Hanselmann
          fields = line.split()
1133 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
1134 899d2a81 Michael Hanselmann
            names = fields[1:]
1135 899d2a81 Michael Hanselmann
            if hostname in names:
1136 899d2a81 Michael Hanselmann
              while hostname in names:
1137 899d2a81 Michael Hanselmann
                names.remove(hostname)
1138 899d2a81 Michael Hanselmann
              if names:
1139 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1140 899d2a81 Michael Hanselmann
              continue
1141 899d2a81 Michael Hanselmann
1142 899d2a81 Michael Hanselmann
          out.write(line)
1143 59f82e3f Michael Hanselmann
1144 59f82e3f Michael Hanselmann
        out.flush()
1145 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1146 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1147 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1148 59f82e3f Michael Hanselmann
      finally:
1149 59f82e3f Michael Hanselmann
        f.close()
1150 a8083063 Iustin Pop
    finally:
1151 59f82e3f Michael Hanselmann
      out.close()
1152 59f82e3f Michael Hanselmann
  except:
1153 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1154 59f82e3f Michael Hanselmann
    raise
1155 a8083063 Iustin Pop
1156 a8083063 Iustin Pop
1157 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1158 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1159 d9c02ca6 Michael Hanselmann

1160 58885d79 Iustin Pop
  @type hostname: str
1161 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1162 58885d79 Iustin Pop
      full and shot name will be removed from
1163 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1164 58885d79 Iustin Pop

1165 d9c02ca6 Michael Hanselmann
  """
1166 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1167 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1168 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1169 d9c02ca6 Michael Hanselmann
1170 d9c02ca6 Michael Hanselmann
1171 1d466a4f Michael Hanselmann
def TimestampForFilename():
1172 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1173 1d466a4f Michael Hanselmann

1174 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1175 1d466a4f Michael Hanselmann
  separators.
1176 1d466a4f Michael Hanselmann

1177 1d466a4f Michael Hanselmann
  """
1178 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1179 1d466a4f Michael Hanselmann
1180 1d466a4f Michael Hanselmann
1181 a8083063 Iustin Pop
def CreateBackup(file_name):
1182 a8083063 Iustin Pop
  """Creates a backup of a file.
1183 a8083063 Iustin Pop

1184 58885d79 Iustin Pop
  @type file_name: str
1185 58885d79 Iustin Pop
  @param file_name: file to be backed up
1186 58885d79 Iustin Pop
  @rtype: str
1187 58885d79 Iustin Pop
  @return: the path to the newly created backup
1188 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1189 a8083063 Iustin Pop

1190 a8083063 Iustin Pop
  """
1191 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1192 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1193 3ecf6786 Iustin Pop
                                file_name)
1194 a8083063 Iustin Pop
1195 1d466a4f Michael Hanselmann
  prefix = ("%s.backup-%s." %
1196 1d466a4f Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
1197 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1198 081b1e69 Michael Hanselmann
1199 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1200 081b1e69 Michael Hanselmann
  try:
1201 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1202 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1203 081b1e69 Michael Hanselmann
    try:
1204 1d466a4f Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
1205 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1206 081b1e69 Michael Hanselmann
    finally:
1207 081b1e69 Michael Hanselmann
      fdst.close()
1208 081b1e69 Michael Hanselmann
  finally:
1209 081b1e69 Michael Hanselmann
    fsrc.close()
1210 081b1e69 Michael Hanselmann
1211 a8083063 Iustin Pop
  return backup_name
1212 a8083063 Iustin Pop
1213 a8083063 Iustin Pop
1214 a8083063 Iustin Pop
def ShellQuote(value):
1215 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1216 3ecf6786 Iustin Pop

1217 58885d79 Iustin Pop
  @type value: str
1218 58885d79 Iustin Pop
  @param value: the argument to be quoted
1219 58885d79 Iustin Pop
  @rtype: str
1220 58885d79 Iustin Pop
  @return: the quoted value
1221 58885d79 Iustin Pop

1222 a8083063 Iustin Pop
  """
1223 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1224 a8083063 Iustin Pop
    return value
1225 a8083063 Iustin Pop
  else:
1226 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1227 a8083063 Iustin Pop
1228 a8083063 Iustin Pop
1229 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1230 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1231 58885d79 Iustin Pop

1232 58885d79 Iustin Pop
  @type args: list
1233 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1234 58885d79 Iustin Pop
  @rtype: str
1235 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1236 a8083063 Iustin Pop

1237 a8083063 Iustin Pop
  """
1238 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1239 88d14415 Michael Hanselmann
1240 88d14415 Michael Hanselmann
1241 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1242 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1243 2c30e9d7 Alexander Schreiber

1244 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1245 58885d79 Iustin Pop
  to it.
1246 58885d79 Iustin Pop

1247 58885d79 Iustin Pop
  @type target: str
1248 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1249 58885d79 Iustin Pop
  @type port: int
1250 58885d79 Iustin Pop
  @param port: the port to connect to
1251 58885d79 Iustin Pop
  @type timeout: int
1252 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1253 58885d79 Iustin Pop
  @type live_port_needed: boolean
1254 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1255 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1256 58885d79 Iustin Pop
  @type source: str or None
1257 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1258 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1259 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1260 2c30e9d7 Alexander Schreiber

1261 2c30e9d7 Alexander Schreiber
  """
1262 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1263 2c30e9d7 Alexander Schreiber
1264 0b5ad33e Iustin Pop
  success = False
1265 2c30e9d7 Alexander Schreiber
1266 b15d625f Iustin Pop
  if source is not None:
1267 b15d625f Iustin Pop
    try:
1268 b15d625f Iustin Pop
      sock.bind((source, 0))
1269 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1270 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1271 b15d625f Iustin Pop
        success = False
1272 2c30e9d7 Alexander Schreiber
1273 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1274 2c30e9d7 Alexander Schreiber
1275 2c30e9d7 Alexander Schreiber
  try:
1276 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1277 2c30e9d7 Alexander Schreiber
    sock.close()
1278 2c30e9d7 Alexander Schreiber
    success = True
1279 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1280 2c30e9d7 Alexander Schreiber
    success = False
1281 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1282 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1283 2c30e9d7 Alexander Schreiber
1284 2c30e9d7 Alexander Schreiber
  return success
1285 eedbda4b Michael Hanselmann
1286 eedbda4b Michael Hanselmann
1287 caad16e2 Iustin Pop
def OwnIpAddress(address):
1288 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1289 caad16e2 Iustin Pop

1290 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1291 caad16e2 Iustin Pop
  address.
1292 caad16e2 Iustin Pop

1293 caad16e2 Iustin Pop
  @type address: string
1294 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1295 caad16e2 Iustin Pop
  @rtype: bool
1296 58885d79 Iustin Pop
  @return: True if we own the address
1297 caad16e2 Iustin Pop

1298 caad16e2 Iustin Pop
  """
1299 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1300 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1301 caad16e2 Iustin Pop
1302 caad16e2 Iustin Pop
1303 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1304 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1305 58885d79 Iustin Pop

1306 58885d79 Iustin Pop
  @type path: str
1307 58885d79 Iustin Pop
  @param path: the directory to enumerate
1308 58885d79 Iustin Pop
  @rtype: list
1309 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1310 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1311 eedbda4b Michael Hanselmann

1312 eedbda4b Michael Hanselmann
  """
1313 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1314 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1315 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1316 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1317 f3299a07 Michael Hanselmann
  files.sort()
1318 f3299a07 Michael Hanselmann
  return files
1319 2f8b60b3 Iustin Pop
1320 2f8b60b3 Iustin Pop
1321 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1322 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1323 257f4c0a Iustin Pop

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

1328 2f8b60b3 Iustin Pop
  """
1329 2f8b60b3 Iustin Pop
  try:
1330 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1331 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1332 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1333 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1334 257f4c0a Iustin Pop
    else:
1335 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1336 257f4c0a Iustin Pop
                                   type(user))
1337 2f8b60b3 Iustin Pop
  except KeyError:
1338 2f8b60b3 Iustin Pop
    return default
1339 2f8b60b3 Iustin Pop
  return result.pw_dir
1340 59072e7e Michael Hanselmann
1341 59072e7e Michael Hanselmann
1342 24818e8f Michael Hanselmann
def NewUUID():
1343 59072e7e Michael Hanselmann
  """Returns a random UUID.
1344 59072e7e Michael Hanselmann

1345 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1346 58885d79 Iustin Pop
      filesystem.
1347 58885d79 Iustin Pop
  @rtype: str
1348 58885d79 Iustin Pop

1349 59072e7e Michael Hanselmann
  """
1350 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1351 087b34fe Iustin Pop
1352 087b34fe Iustin Pop
1353 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1354 33081d90 Iustin Pop
  """Generates a random secret.
1355 33081d90 Iustin Pop

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

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

1364 33081d90 Iustin Pop
  """
1365 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1366 33081d90 Iustin Pop
1367 33081d90 Iustin Pop
1368 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1369 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1370 9dae41ad Guido Trotter

1371 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1372 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1373 9dae41ad Guido Trotter

1374 9dae41ad Guido Trotter
  """
1375 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1376 9dae41ad Guido Trotter
    try:
1377 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1378 9dae41ad Guido Trotter
    except EnvironmentError, err:
1379 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1380 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1381 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1382 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1383 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1384 9dae41ad Guido Trotter
1385 9dae41ad Guido Trotter
1386 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1387 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1388 ca0aa6d0 Michael Hanselmann

1389 016308cb Iustin Pop
  @type size: int
1390 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1391 58885d79 Iustin Pop
  @rtype: str
1392 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1393 ca0aa6d0 Michael Hanselmann

1394 ca0aa6d0 Michael Hanselmann
  """
1395 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1396 ca0aa6d0 Michael Hanselmann
  try:
1397 582ed043 Guido Trotter
    return f.read(size)
1398 ca0aa6d0 Michael Hanselmann
  finally:
1399 ca0aa6d0 Michael Hanselmann
    f.close()
1400 ca0aa6d0 Michael Hanselmann
1401 ca0aa6d0 Michael Hanselmann
1402 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1403 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1404 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1405 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1406 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1407 087b34fe Iustin Pop
  """(Over)write a file atomically.
1408 087b34fe Iustin Pop

1409 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1410 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1411 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1412 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1413 087b34fe Iustin Pop
  mtime/atime of the file.
1414 087b34fe Iustin Pop

1415 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1416 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1417 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1418 087b34fe Iustin Pop
  temporary file should be removed.
1419 087b34fe Iustin Pop

1420 58885d79 Iustin Pop
  @type file_name: str
1421 58885d79 Iustin Pop
  @param file_name: the target filename
1422 58885d79 Iustin Pop
  @type fn: callable
1423 58885d79 Iustin Pop
  @param fn: content writing function, called with
1424 58885d79 Iustin Pop
      file descriptor as parameter
1425 69efe319 Michael Hanselmann
  @type data: str
1426 58885d79 Iustin Pop
  @param data: contents of the file
1427 58885d79 Iustin Pop
  @type mode: int
1428 58885d79 Iustin Pop
  @param mode: file mode
1429 58885d79 Iustin Pop
  @type uid: int
1430 58885d79 Iustin Pop
  @param uid: the owner of the file
1431 58885d79 Iustin Pop
  @type gid: int
1432 58885d79 Iustin Pop
  @param gid: the group of the file
1433 58885d79 Iustin Pop
  @type atime: int
1434 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1435 58885d79 Iustin Pop
  @type mtime: int
1436 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1437 58885d79 Iustin Pop
  @type close: boolean
1438 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1439 58885d79 Iustin Pop
  @type prewrite: callable
1440 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1441 58885d79 Iustin Pop
  @type postwrite: callable
1442 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1443 58885d79 Iustin Pop

1444 58885d79 Iustin Pop
  @rtype: None or int
1445 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1446 58885d79 Iustin Pop
      otherwise the file descriptor
1447 58885d79 Iustin Pop

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

1450 087b34fe Iustin Pop
  """
1451 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1452 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1453 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1454 087b34fe Iustin Pop
1455 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1456 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1457 087b34fe Iustin Pop
1458 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1459 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1460 087b34fe Iustin Pop
                                 " set or None")
1461 087b34fe Iustin Pop
1462 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1463 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1464 087b34fe Iustin Pop
1465 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1466 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1467 81b7354c Iustin Pop
  do_remove = True
1468 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1469 087b34fe Iustin Pop
  # leaves it in place
1470 087b34fe Iustin Pop
  try:
1471 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1472 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1473 087b34fe Iustin Pop
    if mode:
1474 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1475 71714516 Michael Hanselmann
    if callable(prewrite):
1476 71714516 Michael Hanselmann
      prewrite(fd)
1477 087b34fe Iustin Pop
    if data is not None:
1478 087b34fe Iustin Pop
      os.write(fd, data)
1479 087b34fe Iustin Pop
    else:
1480 087b34fe Iustin Pop
      fn(fd)
1481 71714516 Michael Hanselmann
    if callable(postwrite):
1482 71714516 Michael Hanselmann
      postwrite(fd)
1483 087b34fe Iustin Pop
    os.fsync(fd)
1484 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1485 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1486 70f4497c Michael Hanselmann
    if not dry_run:
1487 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1488 81b7354c Iustin Pop
      do_remove = False
1489 087b34fe Iustin Pop
  finally:
1490 71714516 Michael Hanselmann
    if close:
1491 71714516 Michael Hanselmann
      os.close(fd)
1492 71714516 Michael Hanselmann
      result = None
1493 71714516 Michael Hanselmann
    else:
1494 71714516 Michael Hanselmann
      result = fd
1495 81b7354c Iustin Pop
    if do_remove:
1496 81b7354c Iustin Pop
      RemoveFile(new_name)
1497 78feb6fb Guido Trotter
1498 71714516 Michael Hanselmann
  return result
1499 71714516 Michael Hanselmann
1500 78feb6fb Guido Trotter
1501 e587b46a Guido Trotter
def ReadOneLineFile(file_name, strict=False):
1502 e587b46a Guido Trotter
  """Return the first non-empty line from a file.
1503 e587b46a Guido Trotter

1504 e587b46a Guido Trotter
  @type strict: boolean
1505 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1506 e587b46a Guido Trotter
      non-empty line
1507 e587b46a Guido Trotter

1508 e587b46a Guido Trotter
  """
1509 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1510 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1511 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1512 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1513 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1514 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1515 e587b46a Guido Trotter
                              file_name)
1516 e587b46a Guido Trotter
  return full_lines[0]
1517 e587b46a Guido Trotter
1518 e587b46a Guido Trotter
1519 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1520 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1521 7b4126b7 Iustin Pop

1522 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1523 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1524 7b4126b7 Iustin Pop
  value, the index will be returned.
1525 7b4126b7 Iustin Pop

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

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

1531 58885d79 Iustin Pop
  @type seq: sequence
1532 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1533 58885d79 Iustin Pop
  @type base: int
1534 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1535 58885d79 Iustin Pop
  @rtype: int
1536 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1537 7b4126b7 Iustin Pop

1538 7b4126b7 Iustin Pop
  """
1539 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1540 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1541 7b4126b7 Iustin Pop
    if elem > idx + base:
1542 7b4126b7 Iustin Pop
      # idx is not used
1543 7b4126b7 Iustin Pop
      return idx + base
1544 7b4126b7 Iustin Pop
  return None
1545 7b4126b7 Iustin Pop
1546 7b4126b7 Iustin Pop
1547 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1548 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1549 dcd511c8 Guido Trotter

1550 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1551 dfdc4060 Guido Trotter

1552 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1553 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1554 dfdc4060 Guido Trotter
  @type event: integer
1555 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
1556 dcd511c8 Guido Trotter
  @type timeout: float or None
1557 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
1558 dcd511c8 Guido Trotter
  @rtype: int or None
1559 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1560 dcd511c8 Guido Trotter

1561 dcd511c8 Guido Trotter
  """
1562 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1563 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1564 dcd511c8 Guido Trotter
1565 dcd511c8 Guido Trotter
  if timeout is not None:
1566 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1567 dcd511c8 Guido Trotter
    timeout *= 1000
1568 dcd511c8 Guido Trotter
1569 dcd511c8 Guido Trotter
  poller = select.poll()
1570 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1571 dcd511c8 Guido Trotter
  try:
1572 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1573 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1574 dfdc4060 Guido Trotter
    # every so often.
1575 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1576 dfdc4060 Guido Trotter
  except select.error, err:
1577 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1578 dfdc4060 Guido Trotter
      raise
1579 dfdc4060 Guido Trotter
    io_events = []
1580 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1581 dfdc4060 Guido Trotter
    return io_events[0][1]
1582 dfdc4060 Guido Trotter
  else:
1583 dfdc4060 Guido Trotter
    return None
1584 dfdc4060 Guido Trotter
1585 dfdc4060 Guido Trotter
1586 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1587 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1588 dfdc4060 Guido Trotter

1589 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1590 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1591 dfdc4060 Guido Trotter
  expired.
1592 dfdc4060 Guido Trotter

1593 dfdc4060 Guido Trotter
  """
1594 dfdc4060 Guido Trotter
1595 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1596 dfdc4060 Guido Trotter
    self.timeout = timeout
1597 dfdc4060 Guido Trotter
1598 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1599 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1600 dfdc4060 Guido Trotter
    if result is None:
1601 dfdc4060 Guido Trotter
      raise RetryAgain()
1602 dfdc4060 Guido Trotter
    else:
1603 dfdc4060 Guido Trotter
      return result
1604 dfdc4060 Guido Trotter
1605 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1606 dfdc4060 Guido Trotter
    self.timeout = timeout
1607 dfdc4060 Guido Trotter
1608 dfdc4060 Guido Trotter
1609 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1610 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1611 dfdc4060 Guido Trotter

1612 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1613 dfdc4060 Guido Trotter

1614 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1615 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1616 dfdc4060 Guido Trotter
  @type event: integer
1617 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1618 dfdc4060 Guido Trotter
  @type timeout: float or None
1619 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1620 dfdc4060 Guido Trotter
  @rtype: int or None
1621 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1622 dfdc4060 Guido Trotter

1623 dfdc4060 Guido Trotter
  """
1624 dfdc4060 Guido Trotter
  if timeout is not None:
1625 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1626 1b429e2a Iustin Pop
    try:
1627 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1628 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1629 1b429e2a Iustin Pop
    except RetryTimeout:
1630 1b429e2a Iustin Pop
      result = None
1631 dfdc4060 Guido Trotter
  else:
1632 dfdc4060 Guido Trotter
    result = None
1633 dfdc4060 Guido Trotter
    while result is None:
1634 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1635 dfdc4060 Guido Trotter
  return result
1636 dcd511c8 Guido Trotter
1637 dcd511c8 Guido Trotter
1638 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1639 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1640 f7414041 Michael Hanselmann

1641 f7414041 Michael Hanselmann
  Element order is preserved.
1642 58885d79 Iustin Pop

1643 58885d79 Iustin Pop
  @type seq: sequence
1644 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1645 58885d79 Iustin Pop
  @rtype: list
1646 58885d79 Iustin Pop
  @return: list of unique elements from seq
1647 58885d79 Iustin Pop

1648 f7414041 Michael Hanselmann
  """
1649 f7414041 Michael Hanselmann
  seen = set()
1650 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1651 1862d460 Alexander Schreiber
1652 1862d460 Alexander Schreiber
1653 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1654 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1655 1862d460 Alexander Schreiber

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

1659 58885d79 Iustin Pop
  @type mac: str
1660 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1661 82187135 Renรฉ Nussbaumer
  @rtype: str
1662 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1663 82187135 Renรฉ Nussbaumer

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

1666 1862d460 Alexander Schreiber
  """
1667 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1668 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1669 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1670 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1671 82187135 Renรฉ Nussbaumer
1672 82187135 Renรฉ Nussbaumer
  return mac.lower()
1673 06009e27 Iustin Pop
1674 06009e27 Iustin Pop
1675 06009e27 Iustin Pop
def TestDelay(duration):
1676 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1677 06009e27 Iustin Pop

1678 58885d79 Iustin Pop
  @type duration: float
1679 58885d79 Iustin Pop
  @param duration: the sleep duration
1680 58885d79 Iustin Pop
  @rtype: boolean
1681 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1682 58885d79 Iustin Pop

1683 06009e27 Iustin Pop
  """
1684 06009e27 Iustin Pop
  if duration < 0:
1685 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1686 06009e27 Iustin Pop
  time.sleep(duration)
1687 38ea42a1 Iustin Pop
  return True, None
1688 8f765069 Iustin Pop
1689 8f765069 Iustin Pop
1690 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1691 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1692 8f765069 Iustin Pop

1693 7d88772a Iustin Pop
  @type fd: int
1694 7d88772a Iustin Pop
  @param fd: the file descriptor
1695 7d88772a Iustin Pop
  @type retries: int
1696 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1697 7d88772a Iustin Pop
      other error than EBADF
1698 7d88772a Iustin Pop

1699 7d88772a Iustin Pop
  """
1700 7d88772a Iustin Pop
  try:
1701 7d88772a Iustin Pop
    os.close(fd)
1702 7d88772a Iustin Pop
  except OSError, err:
1703 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1704 7d88772a Iustin Pop
      if retries > 0:
1705 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1706 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1707 7d88772a Iustin Pop
    # ignore this and go on
1708 7d88772a Iustin Pop
1709 7d88772a Iustin Pop
1710 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1711 7d88772a Iustin Pop
  """Close file descriptors.
1712 7d88772a Iustin Pop

1713 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1714 7d88772a Iustin Pop
  stdin/out/err).
1715 8f765069 Iustin Pop

1716 58885d79 Iustin Pop
  @type noclose_fds: list or None
1717 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1718 58885d79 Iustin Pop
      that should not be closed
1719 58885d79 Iustin Pop

1720 8f765069 Iustin Pop
  """
1721 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1722 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1723 8f765069 Iustin Pop
    try:
1724 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1725 8f765069 Iustin Pop
      if MAXFD < 0:
1726 8f765069 Iustin Pop
        MAXFD = 1024
1727 8f765069 Iustin Pop
    except OSError:
1728 8f765069 Iustin Pop
      MAXFD = 1024
1729 8f765069 Iustin Pop
  else:
1730 8f765069 Iustin Pop
    MAXFD = 1024
1731 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1732 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1733 7d88772a Iustin Pop
    maxfd = MAXFD
1734 7d88772a Iustin Pop
1735 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1736 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1737 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1738 7d88772a Iustin Pop
      continue
1739 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1740 7d88772a Iustin Pop
1741 7d88772a Iustin Pop
1742 7d88772a Iustin Pop
def Daemonize(logfile):
1743 7d88772a Iustin Pop
  """Daemonize the current process.
1744 7d88772a Iustin Pop

1745 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1746 7d88772a Iustin Pop
  runs it in the background as a daemon.
1747 7d88772a Iustin Pop

1748 7d88772a Iustin Pop
  @type logfile: str
1749 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1750 7d88772a Iustin Pop
  @rtype: int
1751 5fcc718f Iustin Pop
  @return: the value zero
1752 7d88772a Iustin Pop

1753 7d88772a Iustin Pop
  """
1754 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1755 7260cfbe Iustin Pop
  # yes, we really want os._exit
1756 7d88772a Iustin Pop
  UMASK = 077
1757 7d88772a Iustin Pop
  WORKDIR = "/"
1758 8f765069 Iustin Pop
1759 8f765069 Iustin Pop
  # this might fail
1760 8f765069 Iustin Pop
  pid = os.fork()
1761 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1762 8f765069 Iustin Pop
    os.setsid()
1763 8f765069 Iustin Pop
    # this might fail
1764 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1765 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1766 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1767 8f765069 Iustin Pop
      os.umask(UMASK)
1768 8f765069 Iustin Pop
    else:
1769 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1770 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1771 8f765069 Iustin Pop
  else:
1772 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1773 8f765069 Iustin Pop
1774 7d88772a Iustin Pop
  for fd in range(3):
1775 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1776 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1777 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1778 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1779 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1780 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1781 7d88772a Iustin Pop
  os.dup2(1, 2)
1782 8f765069 Iustin Pop
  return 0
1783 57c177af Iustin Pop
1784 57c177af Iustin Pop
1785 53beffbb Iustin Pop
def DaemonPidFileName(name):
1786 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1787 58885d79 Iustin Pop

1788 58885d79 Iustin Pop
  @type name: str
1789 58885d79 Iustin Pop
  @param name: the daemon name
1790 58885d79 Iustin Pop
  @rtype: str
1791 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1792 58885d79 Iustin Pop
      daemon name
1793 b330ac0b Guido Trotter

1794 b330ac0b Guido Trotter
  """
1795 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1796 b330ac0b Guido Trotter
1797 b330ac0b Guido Trotter
1798 2826b361 Guido Trotter
def EnsureDaemon(name):
1799 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1800 2826b361 Guido Trotter

1801 2826b361 Guido Trotter
  """
1802 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1803 2826b361 Guido Trotter
  if result.failed:
1804 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1805 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1806 2826b361 Guido Trotter
    return False
1807 2826b361 Guido Trotter
1808 2826b361 Guido Trotter
  return True
1809 2826b361 Guido Trotter
1810 2826b361 Guido Trotter
1811 b330ac0b Guido Trotter
def WritePidFile(name):
1812 b330ac0b Guido Trotter
  """Write the current process pidfile.
1813 b330ac0b Guido Trotter

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

1816 58885d79 Iustin Pop
  @type name: str
1817 58885d79 Iustin Pop
  @param name: the daemon name to use
1818 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1819 58885d79 Iustin Pop
      points to a live process
1820 b330ac0b Guido Trotter

1821 b330ac0b Guido Trotter
  """
1822 b330ac0b Guido Trotter
  pid = os.getpid()
1823 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1824 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1825 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1826 b330ac0b Guido Trotter
1827 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1828 b330ac0b Guido Trotter
1829 b330ac0b Guido Trotter
1830 b330ac0b Guido Trotter
def RemovePidFile(name):
1831 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1832 b330ac0b Guido Trotter

1833 b330ac0b Guido Trotter
  Any errors are ignored.
1834 b330ac0b Guido Trotter

1835 58885d79 Iustin Pop
  @type name: str
1836 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1837 58885d79 Iustin Pop

1838 b330ac0b Guido Trotter
  """
1839 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1840 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1841 b330ac0b Guido Trotter
  try:
1842 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1843 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1844 b330ac0b Guido Trotter
    pass
1845 b330ac0b Guido Trotter
1846 b330ac0b Guido Trotter
1847 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1848 ff5251bc Iustin Pop
                waitpid=False):
1849 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1850 b2a1f511 Iustin Pop

1851 b2a1f511 Iustin Pop
  @type pid: int
1852 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1853 38206f3c Iustin Pop
  @type signal_: int
1854 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1855 b2a1f511 Iustin Pop
  @type timeout: int
1856 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1857 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1858 b2a1f511 Iustin Pop
                  will be done
1859 ff5251bc Iustin Pop
  @type waitpid: boolean
1860 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1861 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1862 ff5251bc Iustin Pop
      would remain as zombie
1863 b2a1f511 Iustin Pop

1864 b2a1f511 Iustin Pop
  """
1865 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1866 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1867 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1868 ff5251bc Iustin Pop
    if wait:
1869 ff5251bc Iustin Pop
      try:
1870 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1871 ff5251bc Iustin Pop
      except OSError:
1872 ff5251bc Iustin Pop
        pass
1873 ff5251bc Iustin Pop
1874 b2a1f511 Iustin Pop
  if pid <= 0:
1875 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1876 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1877 b2a1f511 Iustin Pop
1878 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1879 b2a1f511 Iustin Pop
    return
1880 31892b4c Michael Hanselmann
1881 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1882 31892b4c Michael Hanselmann
1883 b2a1f511 Iustin Pop
  if timeout <= 0:
1884 b2a1f511 Iustin Pop
    return
1885 7167159a Michael Hanselmann
1886 31892b4c Michael Hanselmann
  def _CheckProcess():
1887 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1888 31892b4c Michael Hanselmann
      return
1889 31892b4c Michael Hanselmann
1890 7167159a Michael Hanselmann
    try:
1891 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1892 7167159a Michael Hanselmann
    except OSError:
1893 31892b4c Michael Hanselmann
      raise RetryAgain()
1894 31892b4c Michael Hanselmann
1895 31892b4c Michael Hanselmann
    if result_pid > 0:
1896 31892b4c Michael Hanselmann
      return
1897 31892b4c Michael Hanselmann
1898 31892b4c Michael Hanselmann
    raise RetryAgain()
1899 31892b4c Michael Hanselmann
1900 31892b4c Michael Hanselmann
  try:
1901 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1902 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1903 31892b4c Michael Hanselmann
  except RetryTimeout:
1904 31892b4c Michael Hanselmann
    pass
1905 7167159a Michael Hanselmann
1906 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1907 7167159a Michael Hanselmann
    # Kill process if it's still alive
1908 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1909 b2a1f511 Iustin Pop
1910 b2a1f511 Iustin Pop
1911 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1912 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1913 57c177af Iustin Pop

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

1917 58885d79 Iustin Pop
  @type name: str
1918 58885d79 Iustin Pop
  @param name: the name to look for
1919 58885d79 Iustin Pop
  @type search_path: str
1920 58885d79 Iustin Pop
  @param search_path: location to start at
1921 58885d79 Iustin Pop
  @type test: callable
1922 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1923 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1924 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1925 58885d79 Iustin Pop
  @rtype: str or None
1926 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1927 57c177af Iustin Pop

1928 57c177af Iustin Pop
  """
1929 f95c81bf Iustin Pop
  # validate the filename mask
1930 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1931 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1932 f95c81bf Iustin Pop
                     name)
1933 f95c81bf Iustin Pop
    return None
1934 f95c81bf Iustin Pop
1935 57c177af Iustin Pop
  for dir_name in search_path:
1936 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1937 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1938 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1939 f95c81bf Iustin Pop
    # basename
1940 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1941 57c177af Iustin Pop
      return item_name
1942 57c177af Iustin Pop
  return None
1943 8d1a2a64 Michael Hanselmann
1944 8d1a2a64 Michael Hanselmann
1945 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1946 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1947 8d1a2a64 Michael Hanselmann

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

1951 58885d79 Iustin Pop
  @type vglist: dict
1952 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1953 58885d79 Iustin Pop
  @type vgname: str
1954 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1955 58885d79 Iustin Pop
  @type minsize: int
1956 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1957 58885d79 Iustin Pop
  @rtype: None or str
1958 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1959 8d1a2a64 Michael Hanselmann

1960 8d1a2a64 Michael Hanselmann
  """
1961 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1962 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1963 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1964 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1965 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1966 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1967 8d1a2a64 Michael Hanselmann
  return None
1968 7996a135 Iustin Pop
1969 7996a135 Iustin Pop
1970 45bc5e4a Michael Hanselmann
def SplitTime(value):
1971 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1972 739be818 Michael Hanselmann

1973 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1974 45bc5e4a Michael Hanselmann
  @type value: int or float
1975 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1976 739be818 Michael Hanselmann

1977 739be818 Michael Hanselmann
  """
1978 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1979 45bc5e4a Michael Hanselmann
1980 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1981 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1982 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1983 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1984 45bc5e4a Michael Hanselmann
1985 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1986 739be818 Michael Hanselmann
1987 739be818 Michael Hanselmann
1988 739be818 Michael Hanselmann
def MergeTime(timetuple):
1989 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1990 739be818 Michael Hanselmann

1991 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1992 739be818 Michael Hanselmann
  @type timetuple: tuple
1993 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1994 739be818 Michael Hanselmann

1995 739be818 Michael Hanselmann
  """
1996 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1997 739be818 Michael Hanselmann
1998 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1999 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2000 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2001 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2002 739be818 Michael Hanselmann
2003 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2004 739be818 Michael Hanselmann
2005 739be818 Michael Hanselmann
2006 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
2007 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
2008 4a8b186a Michael Hanselmann

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

2013 cd50653c Guido Trotter
  @type daemon_name: string
2014 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2015 58885d79 Iustin Pop
  @rtype: int
2016 58885d79 Iustin Pop

2017 4a8b186a Michael Hanselmann
  """
2018 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
2019 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
2020 cd50653c Guido Trotter
2021 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
2022 4a8b186a Michael Hanselmann
  try:
2023 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
2024 4a8b186a Michael Hanselmann
  except socket.error:
2025 cd50653c Guido Trotter
    port = default_port
2026 4a8b186a Michael Hanselmann
2027 4a8b186a Michael Hanselmann
  return port
2028 4a8b186a Michael Hanselmann
2029 4a8b186a Michael Hanselmann
2030 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2031 551b6283 Iustin Pop
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
2032 82d9caef Iustin Pop
  """Configures the logging module.
2033 82d9caef Iustin Pop

2034 58885d79 Iustin Pop
  @type logfile: str
2035 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2036 ea34193f Iustin Pop
  @type debug: integer
2037 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2038 58885d79 Iustin Pop
      only those at C{INFO} and above level
2039 58885d79 Iustin Pop
  @type stderr_logging: boolean
2040 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2041 58885d79 Iustin Pop
  @type program: str
2042 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2043 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2044 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2045 551b6283 Iustin Pop
  @type syslog: string
2046 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2047 551b6283 Iustin Pop
      - if no, syslog is not used
2048 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2049 551b6283 Iustin Pop
      - if only, only syslog is used
2050 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2051 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2052 58885d79 Iustin Pop

2053 82d9caef Iustin Pop
  """
2054 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2055 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2056 d21d09d6 Iustin Pop
  if multithreaded:
2057 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2058 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2059 82d9caef Iustin Pop
  if debug:
2060 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2061 551b6283 Iustin Pop
    # no debug info for syslog loggers
2062 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2063 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2064 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2065 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2066 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2067 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2068 82d9caef Iustin Pop
2069 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2070 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2071 82d9caef Iustin Pop
2072 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2073 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2074 7d88772a Iustin Pop
    handler.close()
2075 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2076 6346a9e5 Michael Hanselmann
2077 82d9caef Iustin Pop
  if stderr_logging:
2078 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2079 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2080 82d9caef Iustin Pop
    if debug:
2081 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2082 82d9caef Iustin Pop
    else:
2083 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2084 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2085 82d9caef Iustin Pop
2086 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2087 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2088 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2089 551b6283 Iustin Pop
                                                    facility)
2090 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2091 551b6283 Iustin Pop
    # Never enable debug over syslog
2092 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2093 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2094 551b6283 Iustin Pop
2095 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2096 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2097 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2098 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2099 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2100 551b6283 Iustin Pop
    try:
2101 551b6283 Iustin Pop
      logfile_handler = logging.FileHandler(logfile)
2102 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2103 551b6283 Iustin Pop
      if debug:
2104 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2105 551b6283 Iustin Pop
      else:
2106 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2107 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2108 551b6283 Iustin Pop
    except EnvironmentError:
2109 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2110 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2111 551b6283 Iustin Pop
      else:
2112 551b6283 Iustin Pop
        # we need to re-raise the exception
2113 551b6283 Iustin Pop
        raise
2114 82d9caef Iustin Pop
2115 016d04b3 Michael Hanselmann
2116 da961187 Guido Trotter
def IsNormAbsPath(path):
2117 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2118 da961187 Guido Trotter

2119 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2120 da961187 Guido Trotter

2121 da961187 Guido Trotter
  """
2122 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2123 82d9caef Iustin Pop
2124 016d04b3 Michael Hanselmann
2125 4bb678e9 Iustin Pop
def PathJoin(*args):
2126 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2127 4bb678e9 Iustin Pop

2128 4bb678e9 Iustin Pop
  Requirements:
2129 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2130 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2131 4bb678e9 Iustin Pop
        since we check for normalization at the end
2132 4bb678e9 Iustin Pop

2133 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2134 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2135 4bb678e9 Iustin Pop

2136 4bb678e9 Iustin Pop
  """
2137 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2138 4bb678e9 Iustin Pop
  assert args
2139 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2140 4bb678e9 Iustin Pop
  root = args[0]
2141 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2142 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2143 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2144 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2145 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2146 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2147 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2148 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2149 4bb678e9 Iustin Pop
  if prefix != root:
2150 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2151 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2152 4bb678e9 Iustin Pop
  return result
2153 4bb678e9 Iustin Pop
2154 4bb678e9 Iustin Pop
2155 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2156 f65f63ef Iustin Pop
  """Return the last lines from a file.
2157 f65f63ef Iustin Pop

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

2162 f65f63ef Iustin Pop
  @param fname: the file name
2163 f65f63ef Iustin Pop
  @type lines: int
2164 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2165 f65f63ef Iustin Pop

2166 f65f63ef Iustin Pop
  """
2167 f65f63ef Iustin Pop
  fd = open(fname, "r")
2168 f65f63ef Iustin Pop
  try:
2169 f65f63ef Iustin Pop
    fd.seek(0, 2)
2170 f65f63ef Iustin Pop
    pos = fd.tell()
2171 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2172 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2173 f65f63ef Iustin Pop
    raw_data = fd.read()
2174 f65f63ef Iustin Pop
  finally:
2175 f65f63ef Iustin Pop
    fd.close()
2176 f65f63ef Iustin Pop
2177 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2178 f65f63ef Iustin Pop
  return rows[-lines:]
2179 f65f63ef Iustin Pop
2180 f65f63ef Iustin Pop
2181 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2182 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2183 27e46076 Michael Hanselmann

2184 27e46076 Michael Hanselmann
  @type value: string
2185 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2186 27e46076 Michael Hanselmann

2187 27e46076 Michael Hanselmann
  """
2188 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2189 27e46076 Michael Hanselmann
  if m:
2190 27e46076 Michael Hanselmann
    # We have an offset
2191 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2192 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2193 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2194 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2195 27e46076 Michael Hanselmann
  else:
2196 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2197 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2198 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2199 27e46076 Michael Hanselmann
    utcoffset = 0
2200 27e46076 Michael Hanselmann
2201 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2202 27e46076 Michael Hanselmann
2203 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2204 27e46076 Michael Hanselmann
2205 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2206 27e46076 Michael Hanselmann
2207 27e46076 Michael Hanselmann
2208 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2209 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2210 27e46076 Michael Hanselmann

2211 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2212 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2213 27e46076 Michael Hanselmann

2214 27e46076 Michael Hanselmann
  """
2215 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2216 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2217 27e46076 Michael Hanselmann
  try:
2218 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2219 27e46076 Michael Hanselmann
  except AttributeError:
2220 27e46076 Michael Hanselmann
    not_before = None
2221 27e46076 Michael Hanselmann
  else:
2222 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2223 27e46076 Michael Hanselmann
2224 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2225 27e46076 Michael Hanselmann
      not_before = None
2226 27e46076 Michael Hanselmann
    else:
2227 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2228 27e46076 Michael Hanselmann
2229 27e46076 Michael Hanselmann
  try:
2230 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2231 27e46076 Michael Hanselmann
  except AttributeError:
2232 27e46076 Michael Hanselmann
    not_after = None
2233 27e46076 Michael Hanselmann
  else:
2234 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2235 27e46076 Michael Hanselmann
2236 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2237 27e46076 Michael Hanselmann
      not_after = None
2238 27e46076 Michael Hanselmann
    else:
2239 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2240 27e46076 Michael Hanselmann
2241 27e46076 Michael Hanselmann
  return (not_before, not_after)
2242 27e46076 Michael Hanselmann
2243 27e46076 Michael Hanselmann
2244 26f15862 Iustin Pop
def SafeEncode(text):
2245 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2246 26f15862 Iustin Pop

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

2256 26f15862 Iustin Pop
  @type text: str or unicode
2257 26f15862 Iustin Pop
  @param text: input data
2258 26f15862 Iustin Pop
  @rtype: str
2259 26f15862 Iustin Pop
  @return: a safe version of text
2260 26f15862 Iustin Pop

2261 26f15862 Iustin Pop
  """
2262 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2263 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2264 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2265 d392fa34 Iustin Pop
  resu = ""
2266 d392fa34 Iustin Pop
  for char in text:
2267 d392fa34 Iustin Pop
    c = ord(char)
2268 d392fa34 Iustin Pop
    if char  == '\t':
2269 d392fa34 Iustin Pop
      resu += r'\t'
2270 d392fa34 Iustin Pop
    elif char == '\n':
2271 d392fa34 Iustin Pop
      resu += r'\n'
2272 d392fa34 Iustin Pop
    elif char == '\r':
2273 d392fa34 Iustin Pop
      resu += r'\'r'
2274 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2275 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2276 d392fa34 Iustin Pop
    else:
2277 d392fa34 Iustin Pop
      resu += char
2278 d392fa34 Iustin Pop
  return resu
2279 26f15862 Iustin Pop
2280 26f15862 Iustin Pop
2281 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2282 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2283 5b69bc7c Iustin Pop

2284 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2285 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2286 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2287 5b69bc7c Iustin Pop
  separator):
2288 5b69bc7c Iustin Pop
    - a plain , separates the elements
2289 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2290 5b69bc7c Iustin Pop
      backslash plus a separator comma
2291 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2292 5b69bc7c Iustin Pop
      non-separator comma
2293 5b69bc7c Iustin Pop

2294 5b69bc7c Iustin Pop
  @type text: string
2295 5b69bc7c Iustin Pop
  @param text: the string to split
2296 5b69bc7c Iustin Pop
  @type sep: string
2297 5b69bc7c Iustin Pop
  @param text: the separator
2298 5b69bc7c Iustin Pop
  @rtype: string
2299 5b69bc7c Iustin Pop
  @return: a list of strings
2300 5b69bc7c Iustin Pop

2301 5b69bc7c Iustin Pop
  """
2302 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2303 5b69bc7c Iustin Pop
  slist = text.split(sep)
2304 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2305 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2306 5b69bc7c Iustin Pop
  rlist = []
2307 5b69bc7c Iustin Pop
  while slist:
2308 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2309 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2310 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2311 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2312 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2313 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2314 5b69bc7c Iustin Pop
        # the next step
2315 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2316 5b69bc7c Iustin Pop
        continue
2317 5b69bc7c Iustin Pop
    rlist.append(e1)
2318 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2319 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2320 5b69bc7c Iustin Pop
  return rlist
2321 5b69bc7c Iustin Pop
2322 5b69bc7c Iustin Pop
2323 ab3e6da8 Iustin Pop
def CommaJoin(names):
2324 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2325 ab3e6da8 Iustin Pop

2326 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2327 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2328 ab3e6da8 Iustin Pop

2329 ab3e6da8 Iustin Pop
  """
2330 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2331 ab3e6da8 Iustin Pop
2332 ab3e6da8 Iustin Pop
2333 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2334 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2335 3f6a47a8 Michael Hanselmann

2336 3f6a47a8 Michael Hanselmann
  @type value: int
2337 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2338 3f6a47a8 Michael Hanselmann
  @rtype: int
2339 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2340 3f6a47a8 Michael Hanselmann

2341 3f6a47a8 Michael Hanselmann
  """
2342 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2343 3f6a47a8 Michael Hanselmann
2344 3f6a47a8 Michael Hanselmann
2345 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2346 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2347 3f6a47a8 Michael Hanselmann

2348 3f6a47a8 Michael Hanselmann
  @type path: string
2349 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2350 3f6a47a8 Michael Hanselmann
  @rtype: int
2351 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2352 3f6a47a8 Michael Hanselmann

2353 3f6a47a8 Michael Hanselmann
  """
2354 3f6a47a8 Michael Hanselmann
  size = 0
2355 3f6a47a8 Michael Hanselmann
2356 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2357 2a887df9 Michael Hanselmann
    for filename in files:
2358 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2359 3f6a47a8 Michael Hanselmann
      size += st.st_size
2360 3f6a47a8 Michael Hanselmann
2361 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2362 3f6a47a8 Michael Hanselmann
2363 3f6a47a8 Michael Hanselmann
2364 620a85fd Iustin Pop
def GetFilesystemStats(path):
2365 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2366 3f6a47a8 Michael Hanselmann

2367 3f6a47a8 Michael Hanselmann
  @type path: string
2368 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2369 3f6a47a8 Michael Hanselmann
  @rtype: int
2370 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2371 3f6a47a8 Michael Hanselmann

2372 3f6a47a8 Michael Hanselmann
  """
2373 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2374 3f6a47a8 Michael Hanselmann
2375 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2376 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2377 620a85fd Iustin Pop
  return (tsize, fsize)
2378 3f6a47a8 Michael Hanselmann
2379 3f6a47a8 Michael Hanselmann
2380 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2381 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2382 eb58f7bd Michael Hanselmann

2383 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2384 eb58f7bd Michael Hanselmann

2385 eb58f7bd Michael Hanselmann
  @type fn: callable
2386 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2387 bdefe5dd Michael Hanselmann
  @rtype: bool
2388 bdefe5dd Michael Hanselmann
  @return: Function's result
2389 eb58f7bd Michael Hanselmann

2390 eb58f7bd Michael Hanselmann
  """
2391 eb58f7bd Michael Hanselmann
  pid = os.fork()
2392 eb58f7bd Michael Hanselmann
  if pid == 0:
2393 eb58f7bd Michael Hanselmann
    # Child process
2394 eb58f7bd Michael Hanselmann
    try:
2395 82869978 Michael Hanselmann
      # In case the function uses temporary files
2396 82869978 Michael Hanselmann
      ResetTempfileModule()
2397 82869978 Michael Hanselmann
2398 eb58f7bd Michael Hanselmann
      # Call function
2399 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2400 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2401 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2402 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2403 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2404 eb58f7bd Michael Hanselmann
      result = 33
2405 eb58f7bd Michael Hanselmann
2406 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2407 eb58f7bd Michael Hanselmann
2408 eb58f7bd Michael Hanselmann
  # Parent process
2409 eb58f7bd Michael Hanselmann
2410 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2411 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2412 eb58f7bd Michael Hanselmann
2413 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2414 eb58f7bd Michael Hanselmann
    exitcode = None
2415 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2416 eb58f7bd Michael Hanselmann
  else:
2417 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2418 eb58f7bd Michael Hanselmann
    signum = None
2419 eb58f7bd Michael Hanselmann
2420 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2421 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2422 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2423 eb58f7bd Michael Hanselmann
2424 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2425 eb58f7bd Michael Hanselmann
2426 eb58f7bd Michael Hanselmann
2427 7996a135 Iustin Pop
def LockedMethod(fn):
2428 7996a135 Iustin Pop
  """Synchronized object access decorator.
2429 7996a135 Iustin Pop

2430 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2431 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2432 7996a135 Iustin Pop

2433 7996a135 Iustin Pop
  """
2434 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2435 e67bd559 Michael Hanselmann
    if debug_locks:
2436 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2437 e67bd559 Michael Hanselmann
2438 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2439 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2440 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2441 7996a135 Iustin Pop
    lock = self._lock
2442 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2443 7996a135 Iustin Pop
    lock.acquire()
2444 7996a135 Iustin Pop
    try:
2445 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2446 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2447 7996a135 Iustin Pop
    finally:
2448 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2449 7996a135 Iustin Pop
      lock.release()
2450 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2451 7996a135 Iustin Pop
    return result
2452 7996a135 Iustin Pop
  return wrapper
2453 eb0f0ce0 Michael Hanselmann
2454 eb0f0ce0 Michael Hanselmann
2455 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2456 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2457 eb0f0ce0 Michael Hanselmann

2458 58885d79 Iustin Pop
  @type fd: int
2459 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2460 58885d79 Iustin Pop

2461 eb0f0ce0 Michael Hanselmann
  """
2462 eb0f0ce0 Michael Hanselmann
  try:
2463 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2464 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2465 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2466 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2467 eb0f0ce0 Michael Hanselmann
    raise
2468 de499029 Michael Hanselmann
2469 de499029 Michael Hanselmann
2470 3b813dd2 Iustin Pop
def FormatTime(val):
2471 3b813dd2 Iustin Pop
  """Formats a time value.
2472 3b813dd2 Iustin Pop

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

2477 3b813dd2 Iustin Pop
  """
2478 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2479 3b813dd2 Iustin Pop
    return "N/A"
2480 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2481 3b813dd2 Iustin Pop
  # platforms
2482 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2483 3b813dd2 Iustin Pop
2484 3b813dd2 Iustin Pop
2485 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2486 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2487 05e50653 Michael Hanselmann

2488 5cbe43a5 Michael Hanselmann
  @type filename: string
2489 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2490 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2491 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2492 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2493 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2494 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2495 5cbe43a5 Michael Hanselmann

2496 05e50653 Michael Hanselmann
  """
2497 05e50653 Michael Hanselmann
  if now is None:
2498 05e50653 Michael Hanselmann
    now = time.time()
2499 05e50653 Michael Hanselmann
2500 05e50653 Michael Hanselmann
  try:
2501 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2502 05e50653 Michael Hanselmann
  except IOError, err:
2503 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2504 05e50653 Michael Hanselmann
      raise
2505 05e50653 Michael Hanselmann
    value = None
2506 05e50653 Michael Hanselmann
2507 05e50653 Michael Hanselmann
  if value is not None:
2508 05e50653 Michael Hanselmann
    try:
2509 05e50653 Michael Hanselmann
      value = int(value)
2510 05e50653 Michael Hanselmann
    except ValueError:
2511 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2512 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2513 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2514 05e50653 Michael Hanselmann
      value = None
2515 05e50653 Michael Hanselmann
2516 05e50653 Michael Hanselmann
    if value is not None:
2517 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2518 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2519 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2520 5cbe43a5 Michael Hanselmann
        value = None
2521 5cbe43a5 Michael Hanselmann
2522 5cbe43a5 Michael Hanselmann
      elif now > value:
2523 05e50653 Michael Hanselmann
        value = None
2524 05e50653 Michael Hanselmann
2525 05e50653 Michael Hanselmann
  return value
2526 05e50653 Michael Hanselmann
2527 05e50653 Michael Hanselmann
2528 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2529 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2530 de0ea66b Michael Hanselmann

2531 40bc67e1 Guido Trotter
  Any arguments which was passed by the retried function to RetryAgain will be
2532 40bc67e1 Guido Trotter
  preserved in RetryTimeout, if it is raised. If such argument was an exception
2533 40bc67e1 Guido Trotter
  the RaiseInner helper method will reraise it.
2534 40bc67e1 Guido Trotter

2535 de0ea66b Michael Hanselmann
  """
2536 506be7c5 Guido Trotter
  def RaiseInner(self):
2537 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
2538 506be7c5 Guido Trotter
      raise self.args[0]
2539 506be7c5 Guido Trotter
    else:
2540 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
2541 de0ea66b Michael Hanselmann
2542 de0ea66b Michael Hanselmann
2543 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2544 de0ea66b Michael Hanselmann
  """Retry again.
2545 de0ea66b Michael Hanselmann

2546 40bc67e1 Guido Trotter
  Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as
2547 40bc67e1 Guido Trotter
  arguments to RetryTimeout. If an exception is passed, the RaiseInner() method
2548 40bc67e1 Guido Trotter
  of the RetryTimeout() method can be used to reraise it.
2549 40bc67e1 Guido Trotter

2550 de0ea66b Michael Hanselmann
  """
2551 de0ea66b Michael Hanselmann
2552 de0ea66b Michael Hanselmann
2553 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2554 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2555 de0ea66b Michael Hanselmann

2556 de0ea66b Michael Hanselmann
  """
2557 de0ea66b Michael Hanselmann
  __slots__ = [
2558 de0ea66b Michael Hanselmann
    "_factor",
2559 de0ea66b Michael Hanselmann
    "_limit",
2560 de0ea66b Michael Hanselmann
    "_next",
2561 de0ea66b Michael Hanselmann
    "_start",
2562 de0ea66b Michael Hanselmann
    ]
2563 de0ea66b Michael Hanselmann
2564 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2565 de0ea66b Michael Hanselmann
    """Initializes this class.
2566 de0ea66b Michael Hanselmann

2567 de0ea66b Michael Hanselmann
    @type start: float
2568 de0ea66b Michael Hanselmann
    @param start: Initial delay
2569 de0ea66b Michael Hanselmann
    @type factor: float
2570 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2571 de0ea66b Michael Hanselmann
    @type limit: float or None
2572 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2573 de0ea66b Michael Hanselmann

2574 de0ea66b Michael Hanselmann
    """
2575 de0ea66b Michael Hanselmann
    assert start > 0.0
2576 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2577 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2578 de0ea66b Michael Hanselmann
2579 de0ea66b Michael Hanselmann
    self._start = start
2580 de0ea66b Michael Hanselmann
    self._factor = factor
2581 de0ea66b Michael Hanselmann
    self._limit = limit
2582 de0ea66b Michael Hanselmann
2583 de0ea66b Michael Hanselmann
    self._next = start
2584 de0ea66b Michael Hanselmann
2585 de0ea66b Michael Hanselmann
  def __call__(self):
2586 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2587 de0ea66b Michael Hanselmann

2588 de0ea66b Michael Hanselmann
    """
2589 de0ea66b Michael Hanselmann
    current = self._next
2590 de0ea66b Michael Hanselmann
2591 de0ea66b Michael Hanselmann
    # Update for next run
2592 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2593 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2594 de0ea66b Michael Hanselmann
2595 de0ea66b Michael Hanselmann
    return current
2596 de0ea66b Michael Hanselmann
2597 de0ea66b Michael Hanselmann
2598 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2599 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2600 de0ea66b Michael Hanselmann
2601 de0ea66b Michael Hanselmann
2602 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2603 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2604 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2605 de0ea66b Michael Hanselmann

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

2610 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2611 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2612 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2613 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2614 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2615 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2616 de0ea66b Michael Hanselmann

2617 de0ea66b Michael Hanselmann
  @type fn: callable
2618 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2619 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2620 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2621 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2622 de0ea66b Michael Hanselmann
  @type timeout: float
2623 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2624 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2625 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2626 de0ea66b Michael Hanselmann
  @return: Return value of function
2627 de0ea66b Michael Hanselmann

2628 de0ea66b Michael Hanselmann
  """
2629 de0ea66b Michael Hanselmann
  assert callable(fn)
2630 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2631 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2632 de0ea66b Michael Hanselmann
2633 de0ea66b Michael Hanselmann
  if args is None:
2634 de0ea66b Michael Hanselmann
    args = []
2635 de0ea66b Michael Hanselmann
2636 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2637 de0ea66b Michael Hanselmann
2638 de0ea66b Michael Hanselmann
  if callable(delay):
2639 de0ea66b Michael Hanselmann
    # External function to calculate delay
2640 de0ea66b Michael Hanselmann
    calc_delay = delay
2641 de0ea66b Michael Hanselmann
2642 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2643 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2644 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2645 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2646 de0ea66b Michael Hanselmann
2647 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2648 de0ea66b Michael Hanselmann
    # Always use the remaining time
2649 de0ea66b Michael Hanselmann
    calc_delay = None
2650 de0ea66b Michael Hanselmann
2651 de0ea66b Michael Hanselmann
  else:
2652 de0ea66b Michael Hanselmann
    # Static delay
2653 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2654 de0ea66b Michael Hanselmann
2655 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2656 de0ea66b Michael Hanselmann
2657 de0ea66b Michael Hanselmann
  while True:
2658 506be7c5 Guido Trotter
    retry_args = []
2659 de0ea66b Michael Hanselmann
    try:
2660 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2661 de0ea66b Michael Hanselmann
      return fn(*args)
2662 506be7c5 Guido Trotter
    except RetryAgain, err:
2663 506be7c5 Guido Trotter
      retry_args = err.args
2664 1b429e2a Iustin Pop
    except RetryTimeout:
2665 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
2666 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
2667 de0ea66b Michael Hanselmann
2668 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2669 de0ea66b Michael Hanselmann
2670 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2671 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
2672 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
2673 de0ea66b Michael Hanselmann
2674 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2675 de0ea66b Michael Hanselmann
2676 de0ea66b Michael Hanselmann
    if calc_delay is None:
2677 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2678 de0ea66b Michael Hanselmann
    else:
2679 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2680 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2681 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2682 de0ea66b Michael Hanselmann
2683 de0ea66b Michael Hanselmann
2684 a87b4824 Michael Hanselmann
class FileLock(object):
2685 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2686 a87b4824 Michael Hanselmann

2687 a87b4824 Michael Hanselmann
  """
2688 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2689 58885d79 Iustin Pop
    """Constructor for FileLock.
2690 58885d79 Iustin Pop

2691 b4478d34 Michael Hanselmann
    @type fd: file
2692 b4478d34 Michael Hanselmann
    @param fd: File object
2693 58885d79 Iustin Pop
    @type filename: str
2694 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2695 58885d79 Iustin Pop

2696 58885d79 Iustin Pop
    """
2697 b4478d34 Michael Hanselmann
    self.fd = fd
2698 a87b4824 Michael Hanselmann
    self.filename = filename
2699 b4478d34 Michael Hanselmann
2700 b4478d34 Michael Hanselmann
  @classmethod
2701 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2702 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2703 b4478d34 Michael Hanselmann

2704 b4478d34 Michael Hanselmann
    @type filename: string
2705 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2706 b4478d34 Michael Hanselmann

2707 b4478d34 Michael Hanselmann
    """
2708 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2709 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2710 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2711 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2712 b4478d34 Michael Hanselmann
               filename)
2713 a87b4824 Michael Hanselmann
2714 a87b4824 Michael Hanselmann
  def __del__(self):
2715 a87b4824 Michael Hanselmann
    self.Close()
2716 a87b4824 Michael Hanselmann
2717 a87b4824 Michael Hanselmann
  def Close(self):
2718 58885d79 Iustin Pop
    """Close the file and release the lock.
2719 58885d79 Iustin Pop

2720 58885d79 Iustin Pop
    """
2721 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2722 a87b4824 Michael Hanselmann
      self.fd.close()
2723 a87b4824 Michael Hanselmann
      self.fd = None
2724 a87b4824 Michael Hanselmann
2725 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2726 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2727 aa74b828 Michael Hanselmann

2728 aa74b828 Michael Hanselmann
    @type flag: int
2729 58885d79 Iustin Pop
    @param flag: operation flag
2730 aa74b828 Michael Hanselmann
    @type blocking: bool
2731 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2732 aa74b828 Michael Hanselmann
    @type timeout: None or float
2733 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2734 aa74b828 Michael Hanselmann
                    non-blocking mode).
2735 aa74b828 Michael Hanselmann
    @type errmsg: string
2736 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2737 aa74b828 Michael Hanselmann

2738 aa74b828 Michael Hanselmann
    """
2739 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2740 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2741 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2742 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2743 a87b4824 Michael Hanselmann
2744 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2745 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2746 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2747 a87b4824 Michael Hanselmann
2748 cc4c9b91 Michael Hanselmann
    if timeout is None:
2749 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2750 cc4c9b91 Michael Hanselmann
    else:
2751 cc4c9b91 Michael Hanselmann
      try:
2752 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2753 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2754 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2755 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2756 aa74b828 Michael Hanselmann
2757 cc4c9b91 Michael Hanselmann
  @staticmethod
2758 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2759 cc4c9b91 Michael Hanselmann
    try:
2760 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2761 cc4c9b91 Michael Hanselmann
    except IOError, err:
2762 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2763 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2764 31892b4c Michael Hanselmann
2765 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2766 cc4c9b91 Michael Hanselmann
      raise
2767 aa74b828 Michael Hanselmann
2768 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2769 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2770 a87b4824 Michael Hanselmann

2771 58885d79 Iustin Pop
    @type blocking: boolean
2772 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2773 58885d79 Iustin Pop
        can lock the file or return immediately
2774 58885d79 Iustin Pop
    @type timeout: int or None
2775 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2776 58885d79 Iustin Pop
        (in blocking mode)
2777 58885d79 Iustin Pop

2778 a87b4824 Michael Hanselmann
    """
2779 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2780 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2781 a87b4824 Michael Hanselmann
2782 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2783 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2784 a87b4824 Michael Hanselmann

2785 58885d79 Iustin Pop
    @type blocking: boolean
2786 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2787 58885d79 Iustin Pop
        can lock the file or return immediately
2788 58885d79 Iustin Pop
    @type timeout: int or None
2789 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2790 58885d79 Iustin Pop
        (in blocking mode)
2791 58885d79 Iustin Pop

2792 a87b4824 Michael Hanselmann
    """
2793 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2794 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2795 a87b4824 Michael Hanselmann
2796 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2797 a87b4824 Michael Hanselmann
    """Unlocks the file.
2798 a87b4824 Michael Hanselmann

2799 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2800 58885d79 Iustin Pop
    operation::
2801 58885d79 Iustin Pop

2802 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2803 58885d79 Iustin Pop
      operations.
2804 58885d79 Iustin Pop

2805 58885d79 Iustin Pop
    @type blocking: boolean
2806 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2807 58885d79 Iustin Pop
        can lock the file or return immediately
2808 58885d79 Iustin Pop
    @type timeout: int or None
2809 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2810 58885d79 Iustin Pop
        (in blocking mode)
2811 a87b4824 Michael Hanselmann

2812 a87b4824 Michael Hanselmann
    """
2813 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2814 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2815 a87b4824 Michael Hanselmann
2816 a87b4824 Michael Hanselmann
2817 339be5a8 Michael Hanselmann
class LineSplitter:
2818 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
2819 339be5a8 Michael Hanselmann

2820 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
2821 339be5a8 Michael Hanselmann

2822 339be5a8 Michael Hanselmann
  """
2823 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
2824 339be5a8 Michael Hanselmann
    """Initializes this class.
2825 339be5a8 Michael Hanselmann

2826 339be5a8 Michael Hanselmann
    @type line_fn: callable
2827 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
2828 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
2829 339be5a8 Michael Hanselmann

2830 339be5a8 Michael Hanselmann
    """
2831 339be5a8 Michael Hanselmann
    assert callable(line_fn)
2832 339be5a8 Michael Hanselmann
2833 339be5a8 Michael Hanselmann
    if args:
2834 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
2835 339be5a8 Michael Hanselmann
      self._line_fn = \
2836 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
2837 339be5a8 Michael Hanselmann
    else:
2838 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
2839 339be5a8 Michael Hanselmann
2840 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
2841 339be5a8 Michael Hanselmann
    self._buffer = ""
2842 339be5a8 Michael Hanselmann
2843 339be5a8 Michael Hanselmann
  def write(self, data):
2844 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
2845 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
2846 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
2847 339be5a8 Michael Hanselmann
2848 339be5a8 Michael Hanselmann
  def flush(self):
2849 339be5a8 Michael Hanselmann
    while self._lines:
2850 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
2851 339be5a8 Michael Hanselmann
2852 339be5a8 Michael Hanselmann
  def close(self):
2853 339be5a8 Michael Hanselmann
    self.flush()
2854 339be5a8 Michael Hanselmann
    if self._buffer:
2855 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
2856 339be5a8 Michael Hanselmann
2857 339be5a8 Michael Hanselmann
2858 451575de Guido Trotter
def SignalHandled(signums):
2859 451575de Guido Trotter
  """Signal Handled decoration.
2860 451575de Guido Trotter

2861 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2862 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2863 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2864 451575de Guido Trotter
  objects as values.
2865 451575de Guido Trotter

2866 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2867 451575de Guido Trotter
  with different handlers.
2868 451575de Guido Trotter

2869 451575de Guido Trotter
  @type signums: list
2870 451575de Guido Trotter
  @param signums: signals to intercept
2871 451575de Guido Trotter

2872 451575de Guido Trotter
  """
2873 451575de Guido Trotter
  def wrap(fn):
2874 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2875 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2876 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2877 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2878 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2879 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2880 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2881 451575de Guido Trotter
      else:
2882 451575de Guido Trotter
        signal_handlers = {}
2883 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2884 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2885 451575de Guido Trotter
      try:
2886 451575de Guido Trotter
        for sig in signums:
2887 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2888 451575de Guido Trotter
        return fn(*args, **kwargs)
2889 451575de Guido Trotter
      finally:
2890 451575de Guido Trotter
        sighandler.Reset()
2891 451575de Guido Trotter
    return sig_function
2892 451575de Guido Trotter
  return wrap
2893 451575de Guido Trotter
2894 451575de Guido Trotter
2895 de499029 Michael Hanselmann
class SignalHandler(object):
2896 de499029 Michael Hanselmann
  """Generic signal handler class.
2897 de499029 Michael Hanselmann

2898 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2899 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2900 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2901 58885d79 Iustin Pop
  signal was sent.
2902 58885d79 Iustin Pop

2903 58885d79 Iustin Pop
  @type signum: list
2904 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2905 58885d79 Iustin Pop
  @type called: boolean
2906 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2907 de499029 Michael Hanselmann

2908 de499029 Michael Hanselmann
  """
2909 de499029 Michael Hanselmann
  def __init__(self, signum):
2910 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2911 de499029 Michael Hanselmann

2912 58885d79 Iustin Pop
    @type signum: int or list of ints
2913 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2914 de499029 Michael Hanselmann

2915 de499029 Michael Hanselmann
    """
2916 6c52849e Guido Trotter
    self.signum = set(signum)
2917 de499029 Michael Hanselmann
    self.called = False
2918 de499029 Michael Hanselmann
2919 de499029 Michael Hanselmann
    self._previous = {}
2920 de499029 Michael Hanselmann
    try:
2921 de499029 Michael Hanselmann
      for signum in self.signum:
2922 de499029 Michael Hanselmann
        # Setup handler
2923 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2924 de499029 Michael Hanselmann
        try:
2925 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2926 de499029 Michael Hanselmann
        except:
2927 de499029 Michael Hanselmann
          # Restore previous handler
2928 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2929 de499029 Michael Hanselmann
          raise
2930 de499029 Michael Hanselmann
    except:
2931 de499029 Michael Hanselmann
      # Reset all handlers
2932 de499029 Michael Hanselmann
      self.Reset()
2933 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2934 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2935 de499029 Michael Hanselmann
      raise
2936 de499029 Michael Hanselmann
2937 de499029 Michael Hanselmann
  def __del__(self):
2938 de499029 Michael Hanselmann
    self.Reset()
2939 de499029 Michael Hanselmann
2940 de499029 Michael Hanselmann
  def Reset(self):
2941 de499029 Michael Hanselmann
    """Restore previous handler.
2942 de499029 Michael Hanselmann

2943 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2944 58885d79 Iustin Pop

2945 de499029 Michael Hanselmann
    """
2946 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2947 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2948 de499029 Michael Hanselmann
      # If successful, remove from dict
2949 de499029 Michael Hanselmann
      del self._previous[signum]
2950 de499029 Michael Hanselmann
2951 de499029 Michael Hanselmann
  def Clear(self):
2952 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2953 de499029 Michael Hanselmann

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

2956 de499029 Michael Hanselmann
    """
2957 de499029 Michael Hanselmann
    self.called = False
2958 de499029 Michael Hanselmann
2959 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2960 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2961 de499029 Michael Hanselmann
    """Actual signal handling function.
2962 de499029 Michael Hanselmann

2963 de499029 Michael Hanselmann
    """
2964 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2965 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2966 de499029 Michael Hanselmann
    self.called = True
2967 a2d2e1a7 Iustin Pop
2968 a2d2e1a7 Iustin Pop
2969 a2d2e1a7 Iustin Pop
class FieldSet(object):
2970 a2d2e1a7 Iustin Pop
  """A simple field set.
2971 a2d2e1a7 Iustin Pop

2972 a2d2e1a7 Iustin Pop
  Among the features are:
2973 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2974 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2975 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2976 a2d2e1a7 Iustin Pop

2977 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2978 a2d2e1a7 Iustin Pop

2979 a2d2e1a7 Iustin Pop
  """
2980 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2981 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2982 a2d2e1a7 Iustin Pop
2983 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2984 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2985 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2986 a2d2e1a7 Iustin Pop
2987 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2988 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2989 a2d2e1a7 Iustin Pop

2990 a2d2e1a7 Iustin Pop
    @type field: str
2991 a2d2e1a7 Iustin Pop
    @param field: the string to match
2992 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2993 a2d2e1a7 Iustin Pop

2994 a2d2e1a7 Iustin Pop
    """
2995 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2996 a2d2e1a7 Iustin Pop
      return m
2997 6c881c52 Iustin Pop
    return None
2998 a2d2e1a7 Iustin Pop
2999 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3000 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3001 a2d2e1a7 Iustin Pop

3002 a2d2e1a7 Iustin Pop
    @type items: list
3003 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3004 a2d2e1a7 Iustin Pop
    @rtype: list
3005 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3006 a2d2e1a7 Iustin Pop

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