Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 196ec587

History | View | Annotate | Download (84.6 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 4b6fa0bf Luca Bigliardi
try:
61 4b6fa0bf Luca Bigliardi
  import ctypes
62 4b6fa0bf Luca Bigliardi
except ImportError:
63 4b6fa0bf Luca Bigliardi
  ctypes = None
64 4b6fa0bf Luca Bigliardi
65 a8083063 Iustin Pop
from ganeti import errors
66 3aecd2c7 Iustin Pop
from ganeti import constants
67 a8083063 Iustin Pop
68 16abfbc2 Alexander Schreiber
69 a8083063 Iustin Pop
_locksheld = []
70 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
71 a8083063 Iustin Pop
72 e67bd559 Michael Hanselmann
debug_locks = False
73 58885d79 Iustin Pop
74 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
75 b74159ee Iustin Pop
no_fork = False
76 f362096f Iustin Pop
77 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
78 13998ef2 Michael Hanselmann
79 f93f2016 Michael Hanselmann
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
80 f93f2016 Michael Hanselmann
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
81 f93f2016 Michael Hanselmann
#
82 f93f2016 Michael Hanselmann
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
83 f93f2016 Michael Hanselmann
# pid_t to "int".
84 f93f2016 Michael Hanselmann
#
85 f93f2016 Michael Hanselmann
# IEEE Std 1003.1-2008:
86 f93f2016 Michael Hanselmann
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
87 f93f2016 Michael Hanselmann
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
88 f93f2016 Michael Hanselmann
_STRUCT_UCRED = "iII"
89 f93f2016 Michael Hanselmann
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
90 f93f2016 Michael Hanselmann
91 4b6fa0bf Luca Bigliardi
# Flags for mlockall() (from bits/mman.h)
92 4b6fa0bf Luca Bigliardi
_MCL_CURRENT = 1
93 4b6fa0bf Luca Bigliardi
_MCL_FUTURE = 2
94 4b6fa0bf Luca Bigliardi
95 7c0d6283 Michael Hanselmann
96 a8083063 Iustin Pop
class RunResult(object):
97 58885d79 Iustin Pop
  """Holds the result of running external programs.
98 58885d79 Iustin Pop

99 58885d79 Iustin Pop
  @type exit_code: int
100 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
101 58885d79 Iustin Pop
      didn't exit())
102 58885d79 Iustin Pop
  @type signal: int or None
103 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
104 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
105 58885d79 Iustin Pop
  @type stdout: str
106 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
107 58885d79 Iustin Pop
  @type stderr: str
108 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
109 58885d79 Iustin Pop
  @type failed: boolean
110 58885d79 Iustin Pop
  @ivar failed: True in case the program was
111 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
112 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
113 a8083063 Iustin Pop

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

141 a8083063 Iustin Pop
    """
142 a8083063 Iustin Pop
    return self.stdout + self.stderr
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
147 bf4daac9 Guido Trotter
def RunCmd(cmd, env=None, output=None, cwd='/', reset_env=False):
148 a8083063 Iustin Pop
  """Execute a (shell) command.
149 a8083063 Iustin Pop

150 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
151 a8083063 Iustin Pop
  closed.
152 a8083063 Iustin Pop

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

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

218 36117c2b Iustin Pop
  @type  cmd: string or list
219 36117c2b Iustin Pop
  @param cmd: Command to run
220 36117c2b Iustin Pop
  @type env: dict
221 36117c2b Iustin Pop
  @param env: The environment to use
222 36117c2b Iustin Pop
  @type via_shell: bool
223 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
224 8797df43 Iustin Pop
  @type cwd: string
225 8797df43 Iustin Pop
  @param cwd: the working directory for the program
226 36117c2b Iustin Pop
  @rtype: tuple
227 36117c2b Iustin Pop
  @return: (out, err, status)
228 36117c2b Iustin Pop

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

287 36117c2b Iustin Pop
  @type  cmd: string or list
288 36117c2b Iustin Pop
  @param cmd: Command to run
289 36117c2b Iustin Pop
  @type env: dict
290 36117c2b Iustin Pop
  @param env: The environment to use
291 36117c2b Iustin Pop
  @type via_shell: bool
292 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
293 36117c2b Iustin Pop
  @type output: str
294 36117c2b Iustin Pop
  @param output: the filename in which to save the output
295 8797df43 Iustin Pop
  @type cwd: string
296 8797df43 Iustin Pop
  @param cwd: the working directory for the program
297 36117c2b Iustin Pop
  @rtype: int
298 36117c2b Iustin Pop
  @return: the exit status
299 36117c2b Iustin Pop

300 36117c2b Iustin Pop
  """
301 36117c2b Iustin Pop
  fh = open(output, "a")
302 36117c2b Iustin Pop
  try:
303 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
304 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
305 36117c2b Iustin Pop
                             stdout=fh,
306 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
307 8797df43 Iustin Pop
                             close_fds=True, env=env,
308 8797df43 Iustin Pop
                             cwd=cwd)
309 36117c2b Iustin Pop
310 36117c2b Iustin Pop
    child.stdin.close()
311 36117c2b Iustin Pop
    status = child.wait()
312 36117c2b Iustin Pop
  finally:
313 36117c2b Iustin Pop
    fh.close()
314 36117c2b Iustin Pop
  return status
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
317 6bb65e3a Guido Trotter
def RunParts(dir_name, env=None, reset_env=False):
318 6bb65e3a Guido Trotter
  """Run Scripts or programs in a directory
319 6bb65e3a Guido Trotter

320 6bb65e3a Guido Trotter
  @type dir_name: string
321 6bb65e3a Guido Trotter
  @param dir_name: absolute path to a directory
322 6bb65e3a Guido Trotter
  @type env: dict
323 6bb65e3a Guido Trotter
  @param env: The environment to use
324 6bb65e3a Guido Trotter
  @type reset_env: boolean
325 6bb65e3a Guido Trotter
  @param reset_env: whether to reset or keep the default os environment
326 6bb65e3a Guido Trotter
  @rtype: list of tuples
327 6bb65e3a Guido Trotter
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
328 6bb65e3a Guido Trotter

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

357 f93f2016 Michael Hanselmann
  @param sock: Unix socket
358 f93f2016 Michael Hanselmann
  @rtype: tuple; (number, number, number)
359 f93f2016 Michael Hanselmann
  @return: The PID, UID and GID of the connected foreign process.
360 f93f2016 Michael Hanselmann

361 f93f2016 Michael Hanselmann
  """
362 f93f2016 Michael Hanselmann
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
363 f93f2016 Michael Hanselmann
                             _STRUCT_UCRED_SIZE)
364 f93f2016 Michael Hanselmann
  return struct.unpack(_STRUCT_UCRED, peercred)
365 f93f2016 Michael Hanselmann
366 f93f2016 Michael Hanselmann
367 a8083063 Iustin Pop
def RemoveFile(filename):
368 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
369 a8083063 Iustin Pop

370 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
371 a8083063 Iustin Pop
  errors are passed.
372 a8083063 Iustin Pop

373 58885d79 Iustin Pop
  @type filename: str
374 58885d79 Iustin Pop
  @param filename: the file to be removed
375 58885d79 Iustin Pop

376 a8083063 Iustin Pop
  """
377 a8083063 Iustin Pop
  try:
378 a8083063 Iustin Pop
    os.unlink(filename)
379 a8083063 Iustin Pop
  except OSError, err:
380 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
381 a8083063 Iustin Pop
      raise
382 a8083063 Iustin Pop
383 a8083063 Iustin Pop
384 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
385 6e797216 Michael Hanselmann
  """Renames a file.
386 6e797216 Michael Hanselmann

387 6e797216 Michael Hanselmann
  @type old: string
388 6e797216 Michael Hanselmann
  @param old: Original path
389 6e797216 Michael Hanselmann
  @type new: string
390 6e797216 Michael Hanselmann
  @param new: New path
391 6e797216 Michael Hanselmann
  @type mkdir: bool
392 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
393 6e797216 Michael Hanselmann
  @type mkdir_mode: int
394 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
395 6e797216 Michael Hanselmann

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

415 76e5f8b5 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
416 76e5f8b5 Michael Hanselmann
  before Python 2.5.
417 76e5f8b5 Michael Hanselmann

418 76e5f8b5 Michael Hanselmann
  """
419 76e5f8b5 Michael Hanselmann
  try:
420 76e5f8b5 Michael Hanselmann
    os.makedirs(path, mode)
421 76e5f8b5 Michael Hanselmann
  except OSError, err:
422 76e5f8b5 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
423 76e5f8b5 Michael Hanselmann
    # Python 2.5 and above.
424 76e5f8b5 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
425 76e5f8b5 Michael Hanselmann
      raise
426 76e5f8b5 Michael Hanselmann
427 76e5f8b5 Michael Hanselmann
428 055f822b Michael Hanselmann
def ResetTempfileModule():
429 055f822b Michael Hanselmann
  """Resets the random name generator of the tempfile module.
430 055f822b Michael Hanselmann

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

437 055f822b Michael Hanselmann
  """
438 055f822b Michael Hanselmann
  # pylint: disable-msg=W0212
439 055f822b Michael Hanselmann
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
440 055f822b Michael Hanselmann
    tempfile._once_lock.acquire()
441 055f822b Michael Hanselmann
    try:
442 055f822b Michael Hanselmann
      # Reset random name generator
443 055f822b Michael Hanselmann
      tempfile._name_sequence = None
444 055f822b Michael Hanselmann
    finally:
445 055f822b Michael Hanselmann
      tempfile._once_lock.release()
446 055f822b Michael Hanselmann
  else:
447 055f822b Michael Hanselmann
    logging.critical("The tempfile module misses at least one of the"
448 055f822b Michael Hanselmann
                     " '_once_lock' and '_name_sequence' attributes")
449 055f822b Michael Hanselmann
450 055f822b Michael Hanselmann
451 a8083063 Iustin Pop
def _FingerprintFile(filename):
452 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
455 a8083063 Iustin Pop
  instead.
456 a8083063 Iustin Pop

457 58885d79 Iustin Pop
  @type filename: str
458 58885d79 Iustin Pop
  @param filename: the filename to checksum
459 58885d79 Iustin Pop
  @rtype: str
460 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
461 58885d79 Iustin Pop
      of the file
462 a8083063 Iustin Pop

463 a8083063 Iustin Pop
  """
464 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
465 a8083063 Iustin Pop
    return None
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
  f = open(filename)
468 a8083063 Iustin Pop
469 7ffe8fba Carlos Valiente
  fp = sha1()
470 a8083063 Iustin Pop
  while True:
471 a8083063 Iustin Pop
    data = f.read(4096)
472 a8083063 Iustin Pop
    if not data:
473 a8083063 Iustin Pop
      break
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
    fp.update(data)
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
  return fp.hexdigest()
478 a8083063 Iustin Pop
479 a8083063 Iustin Pop
480 a8083063 Iustin Pop
def FingerprintFiles(files):
481 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
482 a8083063 Iustin Pop

483 58885d79 Iustin Pop
  @type files: list
484 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
485 58885d79 Iustin Pop
  @rtype: dict
486 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
487 58885d79 Iustin Pop
      existing files
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
  """
490 a8083063 Iustin Pop
  ret = {}
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
  for filename in files:
493 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
494 a8083063 Iustin Pop
    if cksum:
495 a8083063 Iustin Pop
      ret[filename] = cksum
496 a8083063 Iustin Pop
497 a8083063 Iustin Pop
  return ret
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
500 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
501 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
502 a5728081 Guido Trotter

503 a5728081 Guido Trotter
  @type target: dict
504 a5728081 Guido Trotter
  @param target: the dict to update
505 a5728081 Guido Trotter
  @type key_types: dict
506 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
507 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
508 a5728081 Guido Trotter
  @type allowed_values: list
509 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
510 a5728081 Guido Trotter

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

570 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
571 44bf25ff Iustin Pop
      will be returned as alive
572 58885d79 Iustin Pop
  @type pid: int
573 58885d79 Iustin Pop
  @param pid: the process ID to check
574 58885d79 Iustin Pop
  @rtype: boolean
575 58885d79 Iustin Pop
  @return: True if the process exists
576 a8083063 Iustin Pop

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

605 58885d79 Iustin Pop
  @type  pidfile: string
606 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
607 58885d79 Iustin Pop
  @rtype: int
608 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
609 d9f311d7 Iustin Pop
           otherwise 0
610 fee80e90 Guido Trotter

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

631 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
632 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
633 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
634 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
635 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
636 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
637 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
638 a8083063 Iustin Pop

639 58885d79 Iustin Pop
  @type key: str
640 58885d79 Iustin Pop
  @param key: the name to be searched
641 58885d79 Iustin Pop
  @type name_list: list
642 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
643 256eb94b Guido Trotter
  @type case_sensitive: boolean
644 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
645 a8083063 Iustin Pop

646 58885d79 Iustin Pop
  @rtype: None or str
647 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
648 58885d79 Iustin Pop
      otherwise the element from the list which matches
649 a8083063 Iustin Pop

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

677 bcf043c9 Iustin Pop
  """
678 26288e68 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
679 26288e68 Iustin Pop
680 89e1fc26 Iustin Pop
  def __init__(self, name=None):
681 bcf043c9 Iustin Pop
    """Initialize the host name object.
682 bcf043c9 Iustin Pop

683 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
684 89e1fc26 Iustin Pop
    name.
685 bcf043c9 Iustin Pop

686 bcf043c9 Iustin Pop
    """
687 89e1fc26 Iustin Pop
    if name is None:
688 89e1fc26 Iustin Pop
      name = self.SysName()
689 89e1fc26 Iustin Pop
690 89e1fc26 Iustin Pop
    self.query = name
691 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
692 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
693 bcf043c9 Iustin Pop
694 c8a0948f Michael Hanselmann
  def ShortName(self):
695 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
696 c8a0948f Michael Hanselmann

697 c8a0948f Michael Hanselmann
    """
698 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
699 c8a0948f Michael Hanselmann
700 89e1fc26 Iustin Pop
  @staticmethod
701 89e1fc26 Iustin Pop
  def SysName():
702 89e1fc26 Iustin Pop
    """Return the current system's name.
703 bcf043c9 Iustin Pop

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

706 89e1fc26 Iustin Pop
    """
707 89e1fc26 Iustin Pop
    return socket.gethostname()
708 a8083063 Iustin Pop
709 89e1fc26 Iustin Pop
  @staticmethod
710 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
711 89e1fc26 Iustin Pop
    """Look up hostname
712 a8083063 Iustin Pop

713 58885d79 Iustin Pop
    @type hostname: str
714 58885d79 Iustin Pop
    @param hostname: hostname to look up
715 89e1fc26 Iustin Pop

716 58885d79 Iustin Pop
    @rtype: tuple
717 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
718 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
719 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
720 89e1fc26 Iustin Pop

721 89e1fc26 Iustin Pop
    """
722 89e1fc26 Iustin Pop
    try:
723 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
724 89e1fc26 Iustin Pop
    except socket.gaierror, err:
725 89e1fc26 Iustin Pop
      # hostname not found in DNS
726 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
727 a8083063 Iustin Pop
728 89e1fc26 Iustin Pop
    return result
729 a8083063 Iustin Pop
730 26288e68 Iustin Pop
  @classmethod
731 26288e68 Iustin Pop
  def NormalizeName(cls, hostname):
732 26288e68 Iustin Pop
    """Validate and normalize the given hostname.
733 26288e68 Iustin Pop

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

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

765 58885d79 Iustin Pop
  @rtype: dict
766 58885d79 Iustin Pop
  @return:
767 58885d79 Iustin Pop
       Dictionary with keys volume name and values
768 58885d79 Iustin Pop
       the size of the volume
769 a8083063 Iustin Pop

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

793 58885d79 Iustin Pop
  @type bridge: str
794 58885d79 Iustin Pop
  @param bridge: the bridge name to check
795 58885d79 Iustin Pop
  @rtype: boolean
796 58885d79 Iustin Pop
  @return: True if it does
797 a8083063 Iustin Pop

798 a8083063 Iustin Pop
  """
799 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
802 a8083063 Iustin Pop
def NiceSort(name_list):
803 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
804 a8083063 Iustin Pop

805 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
806 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
807 58885d79 Iustin Pop
  'a11']}.
808 a8083063 Iustin Pop

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

813 58885d79 Iustin Pop
  @type name_list: list
814 58885d79 Iustin Pop
  @param name_list: the names to be sorted
815 58885d79 Iustin Pop
  @rtype: list
816 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
817 a8083063 Iustin Pop

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

842 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
843 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
844 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
845 58885d79 Iustin Pop
  exceptions are propagated to the caller.
846 58885d79 Iustin Pop

847 58885d79 Iustin Pop
  @type fn: callable
848 58885d79 Iustin Pop
  @param fn: function to apply to the value
849 58885d79 Iustin Pop
  @param val: the value to be converted
850 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
851 58885d79 Iustin Pop
      otherwise the original value.
852 a8083063 Iustin Pop

853 a8083063 Iustin Pop
  """
854 a8083063 Iustin Pop
  try:
855 a8083063 Iustin Pop
    nv = fn(val)
856 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
857 a8083063 Iustin Pop
    nv = val
858 a8083063 Iustin Pop
  return nv
859 a8083063 Iustin Pop
860 a8083063 Iustin Pop
861 a8083063 Iustin Pop
def IsValidIP(ip):
862 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
863 a8083063 Iustin Pop

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

867 58885d79 Iustin Pop
  @type ip: str
868 58885d79 Iustin Pop
  @param ip: the address to be checked
869 58885d79 Iustin Pop
  @rtype: a regular expression match object
870 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
871 58885d79 Iustin Pop
      address is not valid
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
  """
874 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
875 58885d79 Iustin Pop
  #TODO: convert and return only boolean
876 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
877 a8083063 Iustin Pop
878 a8083063 Iustin Pop
879 a8083063 Iustin Pop
def IsValidShellParam(word):
880 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
881 a8083063 Iustin Pop

882 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
883 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
884 a8083063 Iustin Pop
  the actual command.
885 a8083063 Iustin Pop

886 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
887 a8083063 Iustin Pop
  side.
888 a8083063 Iustin Pop

889 58885d79 Iustin Pop
  @type word: str
890 58885d79 Iustin Pop
  @param word: the word to check
891 58885d79 Iustin Pop
  @rtype: boolean
892 58885d79 Iustin Pop
  @return: True if the word is 'safe'
893 58885d79 Iustin Pop

894 a8083063 Iustin Pop
  """
895 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
896 a8083063 Iustin Pop
897 a8083063 Iustin Pop
898 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
899 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
900 a8083063 Iustin Pop

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

906 58885d79 Iustin Pop
  @type template: str
907 58885d79 Iustin Pop
  @param template: the string holding the template for the
908 58885d79 Iustin Pop
      string formatting
909 58885d79 Iustin Pop
  @rtype: str
910 58885d79 Iustin Pop
  @return: the expanded command line
911 58885d79 Iustin Pop

912 a8083063 Iustin Pop
  """
913 a8083063 Iustin Pop
  for word in args:
914 a8083063 Iustin Pop
    if not IsValidShellParam(word):
915 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
916 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
917 a8083063 Iustin Pop
  return template % args
918 a8083063 Iustin Pop
919 a8083063 Iustin Pop
920 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
921 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
922 a8083063 Iustin Pop

923 58885d79 Iustin Pop
  @type value: int
924 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
925 9fbfbb7b Iustin Pop
  @type units: char
926 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
927 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
928 9fbfbb7b Iustin Pop
      - 'm' for MiBs
929 9fbfbb7b Iustin Pop
      - 'g' for GiBs
930 9fbfbb7b Iustin Pop
      - 't' for TiBs
931 58885d79 Iustin Pop
  @rtype: str
932 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
933 a8083063 Iustin Pop

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

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

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

1004 58885d79 Iustin Pop
  @type file_name: str
1005 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1006 58885d79 Iustin Pop
  @type key: str
1007 58885d79 Iustin Pop
  @param key: string containing key
1008 58885d79 Iustin Pop

1009 a8083063 Iustin Pop
  """
1010 a8083063 Iustin Pop
  key_fields = key.split()
1011 a8083063 Iustin Pop
1012 a8083063 Iustin Pop
  f = open(file_name, 'a+')
1013 a8083063 Iustin Pop
  try:
1014 a8083063 Iustin Pop
    nl = True
1015 a8083063 Iustin Pop
    for line in f:
1016 a8083063 Iustin Pop
      # Ignore whitespace changes
1017 a8083063 Iustin Pop
      if line.split() == key_fields:
1018 a8083063 Iustin Pop
        break
1019 a8083063 Iustin Pop
      nl = line.endswith('\n')
1020 a8083063 Iustin Pop
    else:
1021 a8083063 Iustin Pop
      if not nl:
1022 a8083063 Iustin Pop
        f.write("\n")
1023 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
1024 a8083063 Iustin Pop
      f.write("\n")
1025 a8083063 Iustin Pop
      f.flush()
1026 a8083063 Iustin Pop
  finally:
1027 a8083063 Iustin Pop
    f.close()
1028 a8083063 Iustin Pop
1029 a8083063 Iustin Pop
1030 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
1031 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
1032 a8083063 Iustin Pop

1033 58885d79 Iustin Pop
  @type file_name: str
1034 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
1035 58885d79 Iustin Pop
  @type key: str
1036 58885d79 Iustin Pop
  @param key: string containing key
1037 58885d79 Iustin Pop

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

1066 58885d79 Iustin Pop
  @type file_name: str
1067 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1068 58885d79 Iustin Pop
  @type ip: str
1069 58885d79 Iustin Pop
  @param ip: the IP address
1070 58885d79 Iustin Pop
  @type hostname: str
1071 58885d79 Iustin Pop
  @param hostname: the hostname to be added
1072 58885d79 Iustin Pop
  @type aliases: list
1073 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
1074 58885d79 Iustin Pop

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

1113 58885d79 Iustin Pop
  @type hostname: str
1114 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
1115 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1116 58885d79 Iustin Pop

1117 d9c02ca6 Michael Hanselmann
  """
1118 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1119 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1120 d9c02ca6 Michael Hanselmann
1121 d9c02ca6 Michael Hanselmann
1122 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
1123 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
1124 899d2a81 Michael Hanselmann

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

1127 58885d79 Iustin Pop
  @type file_name: str
1128 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1129 58885d79 Iustin Pop
  @type hostname: str
1130 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
1131 58885d79 Iustin Pop

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

1169 58885d79 Iustin Pop
  @type hostname: str
1170 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1171 58885d79 Iustin Pop
      full and shot name will be removed from
1172 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1173 58885d79 Iustin Pop

1174 d9c02ca6 Michael Hanselmann
  """
1175 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1176 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1177 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1178 d9c02ca6 Michael Hanselmann
1179 d9c02ca6 Michael Hanselmann
1180 1d466a4f Michael Hanselmann
def TimestampForFilename():
1181 1d466a4f Michael Hanselmann
  """Returns the current time formatted for filenames.
1182 1d466a4f Michael Hanselmann

1183 1d466a4f Michael Hanselmann
  The format doesn't contain colons as some shells and applications them as
1184 1d466a4f Michael Hanselmann
  separators.
1185 1d466a4f Michael Hanselmann

1186 1d466a4f Michael Hanselmann
  """
1187 1d466a4f Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1188 1d466a4f Michael Hanselmann
1189 1d466a4f Michael Hanselmann
1190 a8083063 Iustin Pop
def CreateBackup(file_name):
1191 a8083063 Iustin Pop
  """Creates a backup of a file.
1192 a8083063 Iustin Pop

1193 58885d79 Iustin Pop
  @type file_name: str
1194 58885d79 Iustin Pop
  @param file_name: file to be backed up
1195 58885d79 Iustin Pop
  @rtype: str
1196 58885d79 Iustin Pop
  @return: the path to the newly created backup
1197 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1198 a8083063 Iustin Pop

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

1226 58885d79 Iustin Pop
  @type value: str
1227 58885d79 Iustin Pop
  @param value: the argument to be quoted
1228 58885d79 Iustin Pop
  @rtype: str
1229 58885d79 Iustin Pop
  @return: the quoted value
1230 58885d79 Iustin Pop

1231 a8083063 Iustin Pop
  """
1232 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1233 a8083063 Iustin Pop
    return value
1234 a8083063 Iustin Pop
  else:
1235 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1236 a8083063 Iustin Pop
1237 a8083063 Iustin Pop
1238 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1239 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1240 58885d79 Iustin Pop

1241 58885d79 Iustin Pop
  @type args: list
1242 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1243 58885d79 Iustin Pop
  @rtype: str
1244 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1245 a8083063 Iustin Pop

1246 a8083063 Iustin Pop
  """
1247 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1248 88d14415 Michael Hanselmann
1249 88d14415 Michael Hanselmann
1250 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1251 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1252 2c30e9d7 Alexander Schreiber

1253 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1254 58885d79 Iustin Pop
  to it.
1255 58885d79 Iustin Pop

1256 58885d79 Iustin Pop
  @type target: str
1257 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1258 58885d79 Iustin Pop
  @type port: int
1259 58885d79 Iustin Pop
  @param port: the port to connect to
1260 58885d79 Iustin Pop
  @type timeout: int
1261 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1262 58885d79 Iustin Pop
  @type live_port_needed: boolean
1263 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1264 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1265 58885d79 Iustin Pop
  @type source: str or None
1266 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1267 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1268 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1269 2c30e9d7 Alexander Schreiber

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

1299 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1300 caad16e2 Iustin Pop
  address.
1301 caad16e2 Iustin Pop

1302 caad16e2 Iustin Pop
  @type address: string
1303 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1304 caad16e2 Iustin Pop
  @rtype: bool
1305 58885d79 Iustin Pop
  @return: True if we own the address
1306 caad16e2 Iustin Pop

1307 caad16e2 Iustin Pop
  """
1308 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1309 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1310 caad16e2 Iustin Pop
1311 caad16e2 Iustin Pop
1312 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1313 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1314 58885d79 Iustin Pop

1315 58885d79 Iustin Pop
  @type path: str
1316 58885d79 Iustin Pop
  @param path: the directory to enumerate
1317 58885d79 Iustin Pop
  @rtype: list
1318 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1319 04a69a18 Iustin Pop
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1320 eedbda4b Michael Hanselmann

1321 eedbda4b Michael Hanselmann
  """
1322 04a69a18 Iustin Pop
  if not IsNormAbsPath(path):
1323 04a69a18 Iustin Pop
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1324 04a69a18 Iustin Pop
                                 " absolute/normalized: '%s'" % path)
1325 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1326 f3299a07 Michael Hanselmann
  files.sort()
1327 f3299a07 Michael Hanselmann
  return files
1328 2f8b60b3 Iustin Pop
1329 2f8b60b3 Iustin Pop
1330 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1331 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1332 257f4c0a Iustin Pop

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

1337 2f8b60b3 Iustin Pop
  """
1338 2f8b60b3 Iustin Pop
  try:
1339 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1340 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1341 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1342 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1343 257f4c0a Iustin Pop
    else:
1344 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1345 257f4c0a Iustin Pop
                                   type(user))
1346 2f8b60b3 Iustin Pop
  except KeyError:
1347 2f8b60b3 Iustin Pop
    return default
1348 2f8b60b3 Iustin Pop
  return result.pw_dir
1349 59072e7e Michael Hanselmann
1350 59072e7e Michael Hanselmann
1351 24818e8f Michael Hanselmann
def NewUUID():
1352 59072e7e Michael Hanselmann
  """Returns a random UUID.
1353 59072e7e Michael Hanselmann

1354 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1355 58885d79 Iustin Pop
      filesystem.
1356 58885d79 Iustin Pop
  @rtype: str
1357 58885d79 Iustin Pop

1358 59072e7e Michael Hanselmann
  """
1359 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1360 087b34fe Iustin Pop
1361 087b34fe Iustin Pop
1362 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1363 33081d90 Iustin Pop
  """Generates a random secret.
1364 33081d90 Iustin Pop

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

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

1373 33081d90 Iustin Pop
  """
1374 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1375 33081d90 Iustin Pop
1376 33081d90 Iustin Pop
1377 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1378 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1379 9dae41ad Guido Trotter

1380 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1381 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1382 9dae41ad Guido Trotter

1383 9dae41ad Guido Trotter
  """
1384 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1385 9dae41ad Guido Trotter
    try:
1386 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1387 9dae41ad Guido Trotter
    except EnvironmentError, err:
1388 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1389 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1390 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1391 b73360e3 Balazs Lecz
    try:
1392 b73360e3 Balazs Lecz
      os.chmod(dir_name, dir_mode)
1393 b73360e3 Balazs Lecz
    except EnvironmentError, err:
1394 b73360e3 Balazs Lecz
      raise errors.GenericError("Cannot change directory permissions on"
1395 b73360e3 Balazs Lecz
                                " '%s': %s" % (dir_name, err))
1396 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1397 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1398 9dae41ad Guido Trotter
1399 9dae41ad Guido Trotter
1400 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1401 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1402 ca0aa6d0 Michael Hanselmann

1403 016308cb Iustin Pop
  @type size: int
1404 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1405 58885d79 Iustin Pop
  @rtype: str
1406 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1407 ca0aa6d0 Michael Hanselmann

1408 ca0aa6d0 Michael Hanselmann
  """
1409 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1410 ca0aa6d0 Michael Hanselmann
  try:
1411 582ed043 Guido Trotter
    return f.read(size)
1412 ca0aa6d0 Michael Hanselmann
  finally:
1413 ca0aa6d0 Michael Hanselmann
    f.close()
1414 ca0aa6d0 Michael Hanselmann
1415 ca0aa6d0 Michael Hanselmann
1416 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1417 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1418 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1419 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1420 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1421 087b34fe Iustin Pop
  """(Over)write a file atomically.
1422 087b34fe Iustin Pop

1423 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1424 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1425 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1426 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1427 087b34fe Iustin Pop
  mtime/atime of the file.
1428 087b34fe Iustin Pop

1429 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1430 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1431 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1432 087b34fe Iustin Pop
  temporary file should be removed.
1433 087b34fe Iustin Pop

1434 58885d79 Iustin Pop
  @type file_name: str
1435 58885d79 Iustin Pop
  @param file_name: the target filename
1436 58885d79 Iustin Pop
  @type fn: callable
1437 58885d79 Iustin Pop
  @param fn: content writing function, called with
1438 58885d79 Iustin Pop
      file descriptor as parameter
1439 69efe319 Michael Hanselmann
  @type data: str
1440 58885d79 Iustin Pop
  @param data: contents of the file
1441 58885d79 Iustin Pop
  @type mode: int
1442 58885d79 Iustin Pop
  @param mode: file mode
1443 58885d79 Iustin Pop
  @type uid: int
1444 58885d79 Iustin Pop
  @param uid: the owner of the file
1445 58885d79 Iustin Pop
  @type gid: int
1446 58885d79 Iustin Pop
  @param gid: the group of the file
1447 58885d79 Iustin Pop
  @type atime: int
1448 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1449 58885d79 Iustin Pop
  @type mtime: int
1450 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1451 58885d79 Iustin Pop
  @type close: boolean
1452 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1453 58885d79 Iustin Pop
  @type prewrite: callable
1454 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1455 58885d79 Iustin Pop
  @type postwrite: callable
1456 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1457 58885d79 Iustin Pop

1458 58885d79 Iustin Pop
  @rtype: None or int
1459 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1460 58885d79 Iustin Pop
      otherwise the file descriptor
1461 58885d79 Iustin Pop

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

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

1518 e587b46a Guido Trotter
  @type strict: boolean
1519 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1520 e587b46a Guido Trotter
      non-empty line
1521 e587b46a Guido Trotter

1522 e587b46a Guido Trotter
  """
1523 e587b46a Guido Trotter
  file_lines = ReadFile(file_name).splitlines()
1524 e587b46a Guido Trotter
  full_lines = filter(bool, file_lines)
1525 e587b46a Guido Trotter
  if not file_lines or not full_lines:
1526 e587b46a Guido Trotter
    raise errors.GenericError("No data in one-liner file %s" % file_name)
1527 e587b46a Guido Trotter
  elif strict and len(full_lines) > 1:
1528 e587b46a Guido Trotter
    raise errors.GenericError("Too many lines in one-liner file %s" %
1529 e587b46a Guido Trotter
                              file_name)
1530 e587b46a Guido Trotter
  return full_lines[0]
1531 e587b46a Guido Trotter
1532 e587b46a Guido Trotter
1533 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1534 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1535 7b4126b7 Iustin Pop

1536 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1537 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1538 7b4126b7 Iustin Pop
  value, the index will be returned.
1539 7b4126b7 Iustin Pop

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

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

1545 58885d79 Iustin Pop
  @type seq: sequence
1546 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1547 58885d79 Iustin Pop
  @type base: int
1548 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1549 58885d79 Iustin Pop
  @rtype: int
1550 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1551 7b4126b7 Iustin Pop

1552 7b4126b7 Iustin Pop
  """
1553 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1554 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1555 7b4126b7 Iustin Pop
    if elem > idx + base:
1556 7b4126b7 Iustin Pop
      # idx is not used
1557 7b4126b7 Iustin Pop
      return idx + base
1558 7b4126b7 Iustin Pop
  return None
1559 7b4126b7 Iustin Pop
1560 7b4126b7 Iustin Pop
1561 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
1562 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
1563 dcd511c8 Guido Trotter

1564 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1565 dfdc4060 Guido Trotter

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

1575 dcd511c8 Guido Trotter
  """
1576 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
1577 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1578 dcd511c8 Guido Trotter
1579 dcd511c8 Guido Trotter
  if timeout is not None:
1580 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
1581 dcd511c8 Guido Trotter
    timeout *= 1000
1582 dcd511c8 Guido Trotter
1583 dcd511c8 Guido Trotter
  poller = select.poll()
1584 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
1585 dcd511c8 Guido Trotter
  try:
1586 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
1587 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
1588 dfdc4060 Guido Trotter
    # every so often.
1589 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
1590 dfdc4060 Guido Trotter
  except select.error, err:
1591 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
1592 dfdc4060 Guido Trotter
      raise
1593 dfdc4060 Guido Trotter
    io_events = []
1594 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
1595 dfdc4060 Guido Trotter
    return io_events[0][1]
1596 dfdc4060 Guido Trotter
  else:
1597 dfdc4060 Guido Trotter
    return None
1598 dfdc4060 Guido Trotter
1599 dfdc4060 Guido Trotter
1600 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
1601 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
1602 dfdc4060 Guido Trotter

1603 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1604 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1605 dfdc4060 Guido Trotter
  expired.
1606 dfdc4060 Guido Trotter

1607 dfdc4060 Guido Trotter
  """
1608 dfdc4060 Guido Trotter
1609 dfdc4060 Guido Trotter
  def __init__(self, timeout):
1610 dfdc4060 Guido Trotter
    self.timeout = timeout
1611 dfdc4060 Guido Trotter
1612 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
1613 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1614 dfdc4060 Guido Trotter
    if result is None:
1615 dfdc4060 Guido Trotter
      raise RetryAgain()
1616 dfdc4060 Guido Trotter
    else:
1617 dfdc4060 Guido Trotter
      return result
1618 dfdc4060 Guido Trotter
1619 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
1620 dfdc4060 Guido Trotter
    self.timeout = timeout
1621 dfdc4060 Guido Trotter
1622 dfdc4060 Guido Trotter
1623 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
1624 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
1625 dfdc4060 Guido Trotter

1626 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1627 dfdc4060 Guido Trotter

1628 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
1629 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
1630 dfdc4060 Guido Trotter
  @type event: integer
1631 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
1632 dfdc4060 Guido Trotter
  @type timeout: float or None
1633 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
1634 dfdc4060 Guido Trotter
  @rtype: int or None
1635 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
1636 dfdc4060 Guido Trotter

1637 dfdc4060 Guido Trotter
  """
1638 dfdc4060 Guido Trotter
  if timeout is not None:
1639 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
1640 1b429e2a Iustin Pop
    try:
1641 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1642 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1643 1b429e2a Iustin Pop
    except RetryTimeout:
1644 1b429e2a Iustin Pop
      result = None
1645 dfdc4060 Guido Trotter
  else:
1646 dfdc4060 Guido Trotter
    result = None
1647 dfdc4060 Guido Trotter
    while result is None:
1648 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1649 dfdc4060 Guido Trotter
  return result
1650 dcd511c8 Guido Trotter
1651 dcd511c8 Guido Trotter
1652 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1653 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1654 f7414041 Michael Hanselmann

1655 f7414041 Michael Hanselmann
  Element order is preserved.
1656 58885d79 Iustin Pop

1657 58885d79 Iustin Pop
  @type seq: sequence
1658 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1659 58885d79 Iustin Pop
  @rtype: list
1660 58885d79 Iustin Pop
  @return: list of unique elements from seq
1661 58885d79 Iustin Pop

1662 f7414041 Michael Hanselmann
  """
1663 f7414041 Michael Hanselmann
  seen = set()
1664 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1665 1862d460 Alexander Schreiber
1666 1862d460 Alexander Schreiber
1667 82187135 René Nussbaumer
def NormalizeAndValidateMac(mac):
1668 82187135 René Nussbaumer
  """Normalizes and check if a MAC address is valid.
1669 1862d460 Alexander Schreiber

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

1673 58885d79 Iustin Pop
  @type mac: str
1674 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1675 82187135 René Nussbaumer
  @rtype: str
1676 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
1677 82187135 René Nussbaumer

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

1680 1862d460 Alexander Schreiber
  """
1681 82187135 René Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1682 82187135 René Nussbaumer
  if not mac_check.match(mac):
1683 82187135 René Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1684 82187135 René Nussbaumer
                               mac, errors.ECODE_INVAL)
1685 82187135 René Nussbaumer
1686 82187135 René Nussbaumer
  return mac.lower()
1687 06009e27 Iustin Pop
1688 06009e27 Iustin Pop
1689 06009e27 Iustin Pop
def TestDelay(duration):
1690 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1691 06009e27 Iustin Pop

1692 58885d79 Iustin Pop
  @type duration: float
1693 58885d79 Iustin Pop
  @param duration: the sleep duration
1694 58885d79 Iustin Pop
  @rtype: boolean
1695 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1696 58885d79 Iustin Pop

1697 06009e27 Iustin Pop
  """
1698 06009e27 Iustin Pop
  if duration < 0:
1699 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1700 06009e27 Iustin Pop
  time.sleep(duration)
1701 38ea42a1 Iustin Pop
  return True, None
1702 8f765069 Iustin Pop
1703 8f765069 Iustin Pop
1704 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1705 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1706 8f765069 Iustin Pop

1707 7d88772a Iustin Pop
  @type fd: int
1708 7d88772a Iustin Pop
  @param fd: the file descriptor
1709 7d88772a Iustin Pop
  @type retries: int
1710 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1711 7d88772a Iustin Pop
      other error than EBADF
1712 7d88772a Iustin Pop

1713 7d88772a Iustin Pop
  """
1714 7d88772a Iustin Pop
  try:
1715 7d88772a Iustin Pop
    os.close(fd)
1716 7d88772a Iustin Pop
  except OSError, err:
1717 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1718 7d88772a Iustin Pop
      if retries > 0:
1719 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1720 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1721 7d88772a Iustin Pop
    # ignore this and go on
1722 7d88772a Iustin Pop
1723 7d88772a Iustin Pop
1724 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1725 7d88772a Iustin Pop
  """Close file descriptors.
1726 7d88772a Iustin Pop

1727 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1728 7d88772a Iustin Pop
  stdin/out/err).
1729 8f765069 Iustin Pop

1730 58885d79 Iustin Pop
  @type noclose_fds: list or None
1731 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1732 58885d79 Iustin Pop
      that should not be closed
1733 58885d79 Iustin Pop

1734 8f765069 Iustin Pop
  """
1735 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1736 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1737 8f765069 Iustin Pop
    try:
1738 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1739 8f765069 Iustin Pop
      if MAXFD < 0:
1740 8f765069 Iustin Pop
        MAXFD = 1024
1741 8f765069 Iustin Pop
    except OSError:
1742 8f765069 Iustin Pop
      MAXFD = 1024
1743 8f765069 Iustin Pop
  else:
1744 8f765069 Iustin Pop
    MAXFD = 1024
1745 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1746 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1747 7d88772a Iustin Pop
    maxfd = MAXFD
1748 7d88772a Iustin Pop
1749 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1750 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1751 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1752 7d88772a Iustin Pop
      continue
1753 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1754 7d88772a Iustin Pop
1755 7d88772a Iustin Pop
1756 4b6fa0bf Luca Bigliardi
def Mlockall():
1757 4b6fa0bf Luca Bigliardi
  """Lock current process' virtual address space into RAM.
1758 4b6fa0bf Luca Bigliardi

1759 4b6fa0bf Luca Bigliardi
  This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
1760 4b6fa0bf Luca Bigliardi
  see mlock(2) for more details. This function requires ctypes module.
1761 4b6fa0bf Luca Bigliardi

1762 4b6fa0bf Luca Bigliardi
  """
1763 4b6fa0bf Luca Bigliardi
  if ctypes is None:
1764 4b6fa0bf Luca Bigliardi
    logging.warning("Cannot set memory lock, ctypes module not found")
1765 4b6fa0bf Luca Bigliardi
    return
1766 4b6fa0bf Luca Bigliardi
1767 4b6fa0bf Luca Bigliardi
  libc = ctypes.cdll.LoadLibrary("libc.so.6")
1768 4b6fa0bf Luca Bigliardi
  if libc is None:
1769 4b6fa0bf Luca Bigliardi
    logging.error("Cannot set memory lock, ctypes cannot load libc")
1770 4b6fa0bf Luca Bigliardi
    return
1771 4b6fa0bf Luca Bigliardi
1772 4b6fa0bf Luca Bigliardi
  # Some older version of the ctypes module don't have built-in functionality
1773 4b6fa0bf Luca Bigliardi
  # to access the errno global variable, where function error codes are stored.
1774 4b6fa0bf Luca Bigliardi
  # By declaring this variable as a pointer to an integer we can then access
1775 4b6fa0bf Luca Bigliardi
  # its value correctly, should the mlockall call fail, in order to see what
1776 4b6fa0bf Luca Bigliardi
  # the actual error code was.
1777 20601361 Luca Bigliardi
  # pylint: disable-msg=W0212
1778 4b6fa0bf Luca Bigliardi
  libc.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
1779 4b6fa0bf Luca Bigliardi
1780 4b6fa0bf Luca Bigliardi
  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
1781 20601361 Luca Bigliardi
    # pylint: disable-msg=W0212
1782 6ed0bbce Luca Bigliardi
    logging.error("Cannot set memory lock: %s",
1783 4b6fa0bf Luca Bigliardi
                  os.strerror(libc.__errno_location().contents.value))
1784 4b6fa0bf Luca Bigliardi
    return
1785 4b6fa0bf Luca Bigliardi
1786 4b6fa0bf Luca Bigliardi
  logging.debug("Memory lock set")
1787 4b6fa0bf Luca Bigliardi
1788 4b6fa0bf Luca Bigliardi
1789 7d88772a Iustin Pop
def Daemonize(logfile):
1790 7d88772a Iustin Pop
  """Daemonize the current process.
1791 7d88772a Iustin Pop

1792 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1793 7d88772a Iustin Pop
  runs it in the background as a daemon.
1794 7d88772a Iustin Pop

1795 7d88772a Iustin Pop
  @type logfile: str
1796 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1797 7d88772a Iustin Pop
  @rtype: int
1798 5fcc718f Iustin Pop
  @return: the value zero
1799 7d88772a Iustin Pop

1800 7d88772a Iustin Pop
  """
1801 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1802 7260cfbe Iustin Pop
  # yes, we really want os._exit
1803 7d88772a Iustin Pop
  UMASK = 077
1804 7d88772a Iustin Pop
  WORKDIR = "/"
1805 8f765069 Iustin Pop
1806 8f765069 Iustin Pop
  # this might fail
1807 8f765069 Iustin Pop
  pid = os.fork()
1808 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1809 8f765069 Iustin Pop
    os.setsid()
1810 8f765069 Iustin Pop
    # this might fail
1811 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1812 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1813 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1814 8f765069 Iustin Pop
      os.umask(UMASK)
1815 8f765069 Iustin Pop
    else:
1816 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1817 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1818 8f765069 Iustin Pop
  else:
1819 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1820 8f765069 Iustin Pop
1821 7d88772a Iustin Pop
  for fd in range(3):
1822 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1823 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1824 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1825 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1826 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1827 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1828 7d88772a Iustin Pop
  os.dup2(1, 2)
1829 8f765069 Iustin Pop
  return 0
1830 57c177af Iustin Pop
1831 57c177af Iustin Pop
1832 53beffbb Iustin Pop
def DaemonPidFileName(name):
1833 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1834 58885d79 Iustin Pop

1835 58885d79 Iustin Pop
  @type name: str
1836 58885d79 Iustin Pop
  @param name: the daemon name
1837 58885d79 Iustin Pop
  @rtype: str
1838 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1839 58885d79 Iustin Pop
      daemon name
1840 b330ac0b Guido Trotter

1841 b330ac0b Guido Trotter
  """
1842 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1843 b330ac0b Guido Trotter
1844 b330ac0b Guido Trotter
1845 2826b361 Guido Trotter
def EnsureDaemon(name):
1846 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1847 2826b361 Guido Trotter

1848 2826b361 Guido Trotter
  """
1849 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1850 2826b361 Guido Trotter
  if result.failed:
1851 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1852 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1853 2826b361 Guido Trotter
    return False
1854 2826b361 Guido Trotter
1855 2826b361 Guido Trotter
  return True
1856 2826b361 Guido Trotter
1857 2826b361 Guido Trotter
1858 b330ac0b Guido Trotter
def WritePidFile(name):
1859 b330ac0b Guido Trotter
  """Write the current process pidfile.
1860 b330ac0b Guido Trotter

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

1863 58885d79 Iustin Pop
  @type name: str
1864 58885d79 Iustin Pop
  @param name: the daemon name to use
1865 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1866 58885d79 Iustin Pop
      points to a live process
1867 b330ac0b Guido Trotter

1868 b330ac0b Guido Trotter
  """
1869 b330ac0b Guido Trotter
  pid = os.getpid()
1870 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1871 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1872 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1873 b330ac0b Guido Trotter
1874 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1875 b330ac0b Guido Trotter
1876 b330ac0b Guido Trotter
1877 b330ac0b Guido Trotter
def RemovePidFile(name):
1878 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1879 b330ac0b Guido Trotter

1880 b330ac0b Guido Trotter
  Any errors are ignored.
1881 b330ac0b Guido Trotter

1882 58885d79 Iustin Pop
  @type name: str
1883 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1884 58885d79 Iustin Pop

1885 b330ac0b Guido Trotter
  """
1886 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1887 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1888 b330ac0b Guido Trotter
  try:
1889 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1890 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1891 b330ac0b Guido Trotter
    pass
1892 b330ac0b Guido Trotter
1893 b330ac0b Guido Trotter
1894 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1895 ff5251bc Iustin Pop
                waitpid=False):
1896 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1897 b2a1f511 Iustin Pop

1898 b2a1f511 Iustin Pop
  @type pid: int
1899 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1900 38206f3c Iustin Pop
  @type signal_: int
1901 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1902 b2a1f511 Iustin Pop
  @type timeout: int
1903 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1904 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1905 b2a1f511 Iustin Pop
                  will be done
1906 ff5251bc Iustin Pop
  @type waitpid: boolean
1907 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1908 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1909 ff5251bc Iustin Pop
      would remain as zombie
1910 b2a1f511 Iustin Pop

1911 b2a1f511 Iustin Pop
  """
1912 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1913 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1914 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1915 ff5251bc Iustin Pop
    if wait:
1916 ff5251bc Iustin Pop
      try:
1917 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1918 ff5251bc Iustin Pop
      except OSError:
1919 ff5251bc Iustin Pop
        pass
1920 ff5251bc Iustin Pop
1921 b2a1f511 Iustin Pop
  if pid <= 0:
1922 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1923 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1924 b2a1f511 Iustin Pop
1925 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1926 b2a1f511 Iustin Pop
    return
1927 31892b4c Michael Hanselmann
1928 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1929 31892b4c Michael Hanselmann
1930 b2a1f511 Iustin Pop
  if timeout <= 0:
1931 b2a1f511 Iustin Pop
    return
1932 7167159a Michael Hanselmann
1933 31892b4c Michael Hanselmann
  def _CheckProcess():
1934 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1935 31892b4c Michael Hanselmann
      return
1936 31892b4c Michael Hanselmann
1937 7167159a Michael Hanselmann
    try:
1938 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1939 7167159a Michael Hanselmann
    except OSError:
1940 31892b4c Michael Hanselmann
      raise RetryAgain()
1941 31892b4c Michael Hanselmann
1942 31892b4c Michael Hanselmann
    if result_pid > 0:
1943 31892b4c Michael Hanselmann
      return
1944 31892b4c Michael Hanselmann
1945 31892b4c Michael Hanselmann
    raise RetryAgain()
1946 31892b4c Michael Hanselmann
1947 31892b4c Michael Hanselmann
  try:
1948 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1949 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1950 31892b4c Michael Hanselmann
  except RetryTimeout:
1951 31892b4c Michael Hanselmann
    pass
1952 7167159a Michael Hanselmann
1953 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1954 7167159a Michael Hanselmann
    # Kill process if it's still alive
1955 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1956 b2a1f511 Iustin Pop
1957 b2a1f511 Iustin Pop
1958 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1959 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1960 57c177af Iustin Pop

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

1964 58885d79 Iustin Pop
  @type name: str
1965 58885d79 Iustin Pop
  @param name: the name to look for
1966 58885d79 Iustin Pop
  @type search_path: str
1967 58885d79 Iustin Pop
  @param search_path: location to start at
1968 58885d79 Iustin Pop
  @type test: callable
1969 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1970 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1971 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1972 58885d79 Iustin Pop
  @rtype: str or None
1973 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1974 57c177af Iustin Pop

1975 57c177af Iustin Pop
  """
1976 f95c81bf Iustin Pop
  # validate the filename mask
1977 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1978 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1979 f95c81bf Iustin Pop
                     name)
1980 f95c81bf Iustin Pop
    return None
1981 f95c81bf Iustin Pop
1982 57c177af Iustin Pop
  for dir_name in search_path:
1983 e02b9114 Iustin Pop
    # FIXME: investigate switch to PathJoin
1984 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1985 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1986 f95c81bf Iustin Pop
    # basename
1987 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1988 57c177af Iustin Pop
      return item_name
1989 57c177af Iustin Pop
  return None
1990 8d1a2a64 Michael Hanselmann
1991 8d1a2a64 Michael Hanselmann
1992 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1993 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1994 8d1a2a64 Michael Hanselmann

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

1998 58885d79 Iustin Pop
  @type vglist: dict
1999 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
2000 58885d79 Iustin Pop
  @type vgname: str
2001 58885d79 Iustin Pop
  @param vgname: the volume group we should check
2002 58885d79 Iustin Pop
  @type minsize: int
2003 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
2004 58885d79 Iustin Pop
  @rtype: None or str
2005 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
2006 8d1a2a64 Michael Hanselmann

2007 8d1a2a64 Michael Hanselmann
  """
2008 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
2009 8d1a2a64 Michael Hanselmann
  if vgsize is None:
2010 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
2011 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
2012 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
2013 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
2014 8d1a2a64 Michael Hanselmann
  return None
2015 7996a135 Iustin Pop
2016 7996a135 Iustin Pop
2017 45bc5e4a Michael Hanselmann
def SplitTime(value):
2018 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
2019 739be818 Michael Hanselmann

2020 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2021 45bc5e4a Michael Hanselmann
  @type value: int or float
2022 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2023 739be818 Michael Hanselmann

2024 739be818 Michael Hanselmann
  """
2025 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
2026 45bc5e4a Michael Hanselmann
2027 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2028 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2029 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2030 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2031 45bc5e4a Michael Hanselmann
2032 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
2033 739be818 Michael Hanselmann
2034 739be818 Michael Hanselmann
2035 739be818 Michael Hanselmann
def MergeTime(timetuple):
2036 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
2037 739be818 Michael Hanselmann

2038 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2039 739be818 Michael Hanselmann
  @type timetuple: tuple
2040 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2041 739be818 Michael Hanselmann

2042 739be818 Michael Hanselmann
  """
2043 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
2044 739be818 Michael Hanselmann
2045 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
2046 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
2047 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
2048 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
2049 739be818 Michael Hanselmann
2050 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
2051 739be818 Michael Hanselmann
2052 739be818 Michael Hanselmann
2053 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
2054 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
2055 4a8b186a Michael Hanselmann

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

2060 cd50653c Guido Trotter
  @type daemon_name: string
2061 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2062 58885d79 Iustin Pop
  @rtype: int
2063 58885d79 Iustin Pop

2064 4a8b186a Michael Hanselmann
  """
2065 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
2066 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
2067 cd50653c Guido Trotter
2068 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
2069 4a8b186a Michael Hanselmann
  try:
2070 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
2071 4a8b186a Michael Hanselmann
  except socket.error:
2072 cd50653c Guido Trotter
    port = default_port
2073 4a8b186a Michael Hanselmann
2074 4a8b186a Michael Hanselmann
  return port
2075 4a8b186a Michael Hanselmann
2076 4a8b186a Michael Hanselmann
2077 de3b8e39 Luca Bigliardi
class LogFileHandler(logging.FileHandler):
2078 de3b8e39 Luca Bigliardi
  """Log handler that doesn't fallback to stderr.
2079 de3b8e39 Luca Bigliardi

2080 de3b8e39 Luca Bigliardi
  When an error occurs while writing on the logfile, logging.FileHandler tries
2081 de3b8e39 Luca Bigliardi
  to log on stderr. This doesn't work in ganeti since stderr is redirected to
2082 de3b8e39 Luca Bigliardi
  the logfile. This class avoids failures reporting errors to /dev/console.
2083 de3b8e39 Luca Bigliardi

2084 de3b8e39 Luca Bigliardi
  """
2085 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2086 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2087 de3b8e39 Luca Bigliardi

2088 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2089 de3b8e39 Luca Bigliardi

2090 de3b8e39 Luca Bigliardi
    """
2091 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2092 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2093 de3b8e39 Luca Bigliardi
2094 20601361 Luca Bigliardi
  def handleError(self, record): # pylint: disable-msg=C0103
2095 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2096 de3b8e39 Luca Bigliardi

2097 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2098 de3b8e39 Luca Bigliardi
    /dev/console.
2099 de3b8e39 Luca Bigliardi

2100 de3b8e39 Luca Bigliardi
    """
2101 de3b8e39 Luca Bigliardi
    try:
2102 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2103 20601361 Luca Bigliardi
    except Exception: # pylint: disable-msg=W0703
2104 de3b8e39 Luca Bigliardi
      try:
2105 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2106 20601361 Luca Bigliardi
      except Exception: # pylint: disable-msg=W0703
2107 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2108 de3b8e39 Luca Bigliardi
        pass
2109 de3b8e39 Luca Bigliardi
2110 de3b8e39 Luca Bigliardi
2111 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2112 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2113 49e60a28 Luca Bigliardi
                 console_logging=False):
2114 82d9caef Iustin Pop
  """Configures the logging module.
2115 82d9caef Iustin Pop

2116 58885d79 Iustin Pop
  @type logfile: str
2117 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
2118 ea34193f Iustin Pop
  @type debug: integer
2119 ea34193f Iustin Pop
  @param debug: if greater than zero, enable debug messages, otherwise
2120 58885d79 Iustin Pop
      only those at C{INFO} and above level
2121 58885d79 Iustin Pop
  @type stderr_logging: boolean
2122 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
2123 58885d79 Iustin Pop
  @type program: str
2124 58885d79 Iustin Pop
  @param program: the name under which we should log messages
2125 d21d09d6 Iustin Pop
  @type multithreaded: boolean
2126 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
2127 551b6283 Iustin Pop
  @type syslog: string
2128 551b6283 Iustin Pop
  @param syslog: one of 'no', 'yes', 'only':
2129 551b6283 Iustin Pop
      - if no, syslog is not used
2130 551b6283 Iustin Pop
      - if yes, syslog is used (in addition to file-logging)
2131 551b6283 Iustin Pop
      - if only, only syslog is used
2132 49e60a28 Luca Bigliardi
  @type console_logging: boolean
2133 49e60a28 Luca Bigliardi
  @param console_logging: if True, will use a FileHandler which falls back to
2134 49e60a28 Luca Bigliardi
      the system console if logging fails
2135 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
2136 551b6283 Iustin Pop
      syslog/stderr logging is disabled
2137 58885d79 Iustin Pop

2138 82d9caef Iustin Pop
  """
2139 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2140 551b6283 Iustin Pop
  sft = program + "[%(process)d]:"
2141 d21d09d6 Iustin Pop
  if multithreaded:
2142 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
2143 551b6283 Iustin Pop
    sft += " (%(threadName)s)"
2144 82d9caef Iustin Pop
  if debug:
2145 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
2146 551b6283 Iustin Pop
    # no debug info for syslog loggers
2147 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
2148 551b6283 Iustin Pop
  # yes, we do want the textual level, as remote syslog will probably
2149 551b6283 Iustin Pop
  # lose the error level, and it's easier to grep for it
2150 551b6283 Iustin Pop
  sft += " %(levelname)s %(message)s"
2151 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
2152 551b6283 Iustin Pop
  sys_fmt = logging.Formatter(sft)
2153 82d9caef Iustin Pop
2154 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
2155 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
2156 82d9caef Iustin Pop
2157 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
2158 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
2159 7d88772a Iustin Pop
    handler.close()
2160 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
2161 6346a9e5 Michael Hanselmann
2162 82d9caef Iustin Pop
  if stderr_logging:
2163 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
2164 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
2165 82d9caef Iustin Pop
    if debug:
2166 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
2167 82d9caef Iustin Pop
    else:
2168 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
2169 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
2170 82d9caef Iustin Pop
2171 551b6283 Iustin Pop
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2172 551b6283 Iustin Pop
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2173 551b6283 Iustin Pop
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2174 551b6283 Iustin Pop
                                                    facility)
2175 551b6283 Iustin Pop
    syslog_handler.setFormatter(sys_fmt)
2176 551b6283 Iustin Pop
    # Never enable debug over syslog
2177 551b6283 Iustin Pop
    syslog_handler.setLevel(logging.INFO)
2178 551b6283 Iustin Pop
    root_logger.addHandler(syslog_handler)
2179 551b6283 Iustin Pop
2180 551b6283 Iustin Pop
  if syslog != constants.SYSLOG_ONLY:
2181 551b6283 Iustin Pop
    # this can fail, if the logging directories are not setup or we have
2182 551b6283 Iustin Pop
    # a permisssion problem; in this case, it's best to log but ignore
2183 551b6283 Iustin Pop
    # the error if stderr_logging is True, and if false we re-raise the
2184 551b6283 Iustin Pop
    # exception since otherwise we could run but without any logs at all
2185 551b6283 Iustin Pop
    try:
2186 49e60a28 Luca Bigliardi
      if console_logging:
2187 49e60a28 Luca Bigliardi
        logfile_handler = LogFileHandler(logfile)
2188 49e60a28 Luca Bigliardi
      else:
2189 49e60a28 Luca Bigliardi
        logfile_handler = logging.FileHandler(logfile)
2190 551b6283 Iustin Pop
      logfile_handler.setFormatter(formatter)
2191 551b6283 Iustin Pop
      if debug:
2192 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.DEBUG)
2193 551b6283 Iustin Pop
      else:
2194 551b6283 Iustin Pop
        logfile_handler.setLevel(logging.INFO)
2195 551b6283 Iustin Pop
      root_logger.addHandler(logfile_handler)
2196 551b6283 Iustin Pop
    except EnvironmentError:
2197 551b6283 Iustin Pop
      if stderr_logging or syslog == constants.SYSLOG_YES:
2198 551b6283 Iustin Pop
        logging.exception("Failed to enable logging to file '%s'", logfile)
2199 551b6283 Iustin Pop
      else:
2200 551b6283 Iustin Pop
        # we need to re-raise the exception
2201 551b6283 Iustin Pop
        raise
2202 82d9caef Iustin Pop
2203 016d04b3 Michael Hanselmann
2204 da961187 Guido Trotter
def IsNormAbsPath(path):
2205 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
2206 da961187 Guido Trotter

2207 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2208 da961187 Guido Trotter

2209 da961187 Guido Trotter
  """
2210 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2211 82d9caef Iustin Pop
2212 016d04b3 Michael Hanselmann
2213 4bb678e9 Iustin Pop
def PathJoin(*args):
2214 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2215 4bb678e9 Iustin Pop

2216 4bb678e9 Iustin Pop
  Requirements:
2217 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2218 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2219 4bb678e9 Iustin Pop
        since we check for normalization at the end
2220 4bb678e9 Iustin Pop

2221 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2222 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2223 4bb678e9 Iustin Pop

2224 4bb678e9 Iustin Pop
  """
2225 4bb678e9 Iustin Pop
  # ensure we're having at least one path passed in
2226 4bb678e9 Iustin Pop
  assert args
2227 4bb678e9 Iustin Pop
  # ensure the first component is an absolute and normalized path name
2228 4bb678e9 Iustin Pop
  root = args[0]
2229 4bb678e9 Iustin Pop
  if not IsNormAbsPath(root):
2230 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2231 4bb678e9 Iustin Pop
  result = os.path.join(*args)
2232 4bb678e9 Iustin Pop
  # ensure that the whole path is normalized
2233 4bb678e9 Iustin Pop
  if not IsNormAbsPath(result):
2234 4bb678e9 Iustin Pop
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2235 4bb678e9 Iustin Pop
  # check that we're still under the original prefix
2236 4bb678e9 Iustin Pop
  prefix = os.path.commonprefix([root, result])
2237 4bb678e9 Iustin Pop
  if prefix != root:
2238 4bb678e9 Iustin Pop
    raise ValueError("Error: path joining resulted in different prefix"
2239 4bb678e9 Iustin Pop
                     " (%s != %s)" % (prefix, root))
2240 4bb678e9 Iustin Pop
  return result
2241 4bb678e9 Iustin Pop
2242 4bb678e9 Iustin Pop
2243 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
2244 f65f63ef Iustin Pop
  """Return the last lines from a file.
2245 f65f63ef Iustin Pop

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

2250 f65f63ef Iustin Pop
  @param fname: the file name
2251 f65f63ef Iustin Pop
  @type lines: int
2252 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2253 f65f63ef Iustin Pop

2254 f65f63ef Iustin Pop
  """
2255 f65f63ef Iustin Pop
  fd = open(fname, "r")
2256 f65f63ef Iustin Pop
  try:
2257 f65f63ef Iustin Pop
    fd.seek(0, 2)
2258 f65f63ef Iustin Pop
    pos = fd.tell()
2259 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
2260 f65f63ef Iustin Pop
    fd.seek(pos, 0)
2261 f65f63ef Iustin Pop
    raw_data = fd.read()
2262 f65f63ef Iustin Pop
  finally:
2263 f65f63ef Iustin Pop
    fd.close()
2264 f65f63ef Iustin Pop
2265 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
2266 f65f63ef Iustin Pop
  return rows[-lines:]
2267 f65f63ef Iustin Pop
2268 f65f63ef Iustin Pop
2269 27e46076 Michael Hanselmann
def _ParseAsn1Generalizedtime(value):
2270 27e46076 Michael Hanselmann
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2271 27e46076 Michael Hanselmann

2272 27e46076 Michael Hanselmann
  @type value: string
2273 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2274 27e46076 Michael Hanselmann

2275 27e46076 Michael Hanselmann
  """
2276 27e46076 Michael Hanselmann
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2277 27e46076 Michael Hanselmann
  if m:
2278 27e46076 Michael Hanselmann
    # We have an offset
2279 27e46076 Michael Hanselmann
    asn1time = m.group(1)
2280 27e46076 Michael Hanselmann
    hours = int(m.group(2))
2281 27e46076 Michael Hanselmann
    minutes = int(m.group(3))
2282 27e46076 Michael Hanselmann
    utcoffset = (60 * hours) + minutes
2283 27e46076 Michael Hanselmann
  else:
2284 27e46076 Michael Hanselmann
    if not value.endswith("Z"):
2285 27e46076 Michael Hanselmann
      raise ValueError("Missing timezone")
2286 27e46076 Michael Hanselmann
    asn1time = value[:-1]
2287 27e46076 Michael Hanselmann
    utcoffset = 0
2288 27e46076 Michael Hanselmann
2289 27e46076 Michael Hanselmann
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2290 27e46076 Michael Hanselmann
2291 27e46076 Michael Hanselmann
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2292 27e46076 Michael Hanselmann
2293 27e46076 Michael Hanselmann
  return calendar.timegm(tt.utctimetuple())
2294 27e46076 Michael Hanselmann
2295 27e46076 Michael Hanselmann
2296 27e46076 Michael Hanselmann
def GetX509CertValidity(cert):
2297 27e46076 Michael Hanselmann
  """Returns the validity period of the certificate.
2298 27e46076 Michael Hanselmann

2299 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2300 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2301 27e46076 Michael Hanselmann

2302 27e46076 Michael Hanselmann
  """
2303 27e46076 Michael Hanselmann
  # The get_notBefore and get_notAfter functions are only supported in
2304 27e46076 Michael Hanselmann
  # pyOpenSSL 0.7 and above.
2305 27e46076 Michael Hanselmann
  try:
2306 27e46076 Michael Hanselmann
    get_notbefore_fn = cert.get_notBefore
2307 27e46076 Michael Hanselmann
  except AttributeError:
2308 27e46076 Michael Hanselmann
    not_before = None
2309 27e46076 Michael Hanselmann
  else:
2310 27e46076 Michael Hanselmann
    not_before_asn1 = get_notbefore_fn()
2311 27e46076 Michael Hanselmann
2312 27e46076 Michael Hanselmann
    if not_before_asn1 is None:
2313 27e46076 Michael Hanselmann
      not_before = None
2314 27e46076 Michael Hanselmann
    else:
2315 27e46076 Michael Hanselmann
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2316 27e46076 Michael Hanselmann
2317 27e46076 Michael Hanselmann
  try:
2318 27e46076 Michael Hanselmann
    get_notafter_fn = cert.get_notAfter
2319 27e46076 Michael Hanselmann
  except AttributeError:
2320 27e46076 Michael Hanselmann
    not_after = None
2321 27e46076 Michael Hanselmann
  else:
2322 27e46076 Michael Hanselmann
    not_after_asn1 = get_notafter_fn()
2323 27e46076 Michael Hanselmann
2324 27e46076 Michael Hanselmann
    if not_after_asn1 is None:
2325 27e46076 Michael Hanselmann
      not_after = None
2326 27e46076 Michael Hanselmann
    else:
2327 27e46076 Michael Hanselmann
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2328 27e46076 Michael Hanselmann
2329 27e46076 Michael Hanselmann
  return (not_before, not_after)
2330 27e46076 Michael Hanselmann
2331 27e46076 Michael Hanselmann
2332 26f15862 Iustin Pop
def SafeEncode(text):
2333 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
2334 26f15862 Iustin Pop

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

2344 26f15862 Iustin Pop
  @type text: str or unicode
2345 26f15862 Iustin Pop
  @param text: input data
2346 26f15862 Iustin Pop
  @rtype: str
2347 26f15862 Iustin Pop
  @return: a safe version of text
2348 26f15862 Iustin Pop

2349 26f15862 Iustin Pop
  """
2350 d392fa34 Iustin Pop
  if isinstance(text, unicode):
2351 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
2352 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
2353 d392fa34 Iustin Pop
  resu = ""
2354 d392fa34 Iustin Pop
  for char in text:
2355 d392fa34 Iustin Pop
    c = ord(char)
2356 d392fa34 Iustin Pop
    if char  == '\t':
2357 d392fa34 Iustin Pop
      resu += r'\t'
2358 d392fa34 Iustin Pop
    elif char == '\n':
2359 d392fa34 Iustin Pop
      resu += r'\n'
2360 d392fa34 Iustin Pop
    elif char == '\r':
2361 d392fa34 Iustin Pop
      resu += r'\'r'
2362 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
2363 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
2364 d392fa34 Iustin Pop
    else:
2365 d392fa34 Iustin Pop
      resu += char
2366 d392fa34 Iustin Pop
  return resu
2367 26f15862 Iustin Pop
2368 26f15862 Iustin Pop
2369 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
2370 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
2371 5b69bc7c Iustin Pop

2372 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2373 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2374 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2375 5b69bc7c Iustin Pop
  separator):
2376 5b69bc7c Iustin Pop
    - a plain , separates the elements
2377 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2378 5b69bc7c Iustin Pop
      backslash plus a separator comma
2379 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2380 5b69bc7c Iustin Pop
      non-separator comma
2381 5b69bc7c Iustin Pop

2382 5b69bc7c Iustin Pop
  @type text: string
2383 5b69bc7c Iustin Pop
  @param text: the string to split
2384 5b69bc7c Iustin Pop
  @type sep: string
2385 5b69bc7c Iustin Pop
  @param text: the separator
2386 5b69bc7c Iustin Pop
  @rtype: string
2387 5b69bc7c Iustin Pop
  @return: a list of strings
2388 5b69bc7c Iustin Pop

2389 5b69bc7c Iustin Pop
  """
2390 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
2391 5b69bc7c Iustin Pop
  slist = text.split(sep)
2392 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
2393 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
2394 5b69bc7c Iustin Pop
  rlist = []
2395 5b69bc7c Iustin Pop
  while slist:
2396 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
2397 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
2398 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
2399 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
2400 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
2401 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
2402 5b69bc7c Iustin Pop
        # the next step
2403 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
2404 5b69bc7c Iustin Pop
        continue
2405 5b69bc7c Iustin Pop
    rlist.append(e1)
2406 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
2407 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2408 5b69bc7c Iustin Pop
  return rlist
2409 5b69bc7c Iustin Pop
2410 5b69bc7c Iustin Pop
2411 ab3e6da8 Iustin Pop
def CommaJoin(names):
2412 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
2413 ab3e6da8 Iustin Pop

2414 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2415 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2416 ab3e6da8 Iustin Pop

2417 ab3e6da8 Iustin Pop
  """
2418 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2419 ab3e6da8 Iustin Pop
2420 ab3e6da8 Iustin Pop
2421 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2422 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2423 3f6a47a8 Michael Hanselmann

2424 3f6a47a8 Michael Hanselmann
  @type value: int
2425 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2426 3f6a47a8 Michael Hanselmann
  @rtype: int
2427 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2428 3f6a47a8 Michael Hanselmann

2429 3f6a47a8 Michael Hanselmann
  """
2430 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2431 3f6a47a8 Michael Hanselmann
2432 3f6a47a8 Michael Hanselmann
2433 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2434 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2435 3f6a47a8 Michael Hanselmann

2436 3f6a47a8 Michael Hanselmann
  @type path: string
2437 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2438 3f6a47a8 Michael Hanselmann
  @rtype: int
2439 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2440 3f6a47a8 Michael Hanselmann

2441 3f6a47a8 Michael Hanselmann
  """
2442 3f6a47a8 Michael Hanselmann
  size = 0
2443 3f6a47a8 Michael Hanselmann
2444 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2445 2a887df9 Michael Hanselmann
    for filename in files:
2446 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2447 3f6a47a8 Michael Hanselmann
      size += st.st_size
2448 3f6a47a8 Michael Hanselmann
2449 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2450 3f6a47a8 Michael Hanselmann
2451 3f6a47a8 Michael Hanselmann
2452 620a85fd Iustin Pop
def GetFilesystemStats(path):
2453 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2454 3f6a47a8 Michael Hanselmann

2455 3f6a47a8 Michael Hanselmann
  @type path: string
2456 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2457 3f6a47a8 Michael Hanselmann
  @rtype: int
2458 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2459 3f6a47a8 Michael Hanselmann

2460 3f6a47a8 Michael Hanselmann
  """
2461 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2462 3f6a47a8 Michael Hanselmann
2463 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2464 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2465 620a85fd Iustin Pop
  return (tsize, fsize)
2466 3f6a47a8 Michael Hanselmann
2467 3f6a47a8 Michael Hanselmann
2468 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2469 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2470 eb58f7bd Michael Hanselmann

2471 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2472 eb58f7bd Michael Hanselmann

2473 eb58f7bd Michael Hanselmann
  @type fn: callable
2474 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2475 bdefe5dd Michael Hanselmann
  @rtype: bool
2476 bdefe5dd Michael Hanselmann
  @return: Function's result
2477 eb58f7bd Michael Hanselmann

2478 eb58f7bd Michael Hanselmann
  """
2479 eb58f7bd Michael Hanselmann
  pid = os.fork()
2480 eb58f7bd Michael Hanselmann
  if pid == 0:
2481 eb58f7bd Michael Hanselmann
    # Child process
2482 eb58f7bd Michael Hanselmann
    try:
2483 82869978 Michael Hanselmann
      # In case the function uses temporary files
2484 82869978 Michael Hanselmann
      ResetTempfileModule()
2485 82869978 Michael Hanselmann
2486 eb58f7bd Michael Hanselmann
      # Call function
2487 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2488 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2489 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2490 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2491 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2492 eb58f7bd Michael Hanselmann
      result = 33
2493 eb58f7bd Michael Hanselmann
2494 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2495 eb58f7bd Michael Hanselmann
2496 eb58f7bd Michael Hanselmann
  # Parent process
2497 eb58f7bd Michael Hanselmann
2498 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2499 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2500 eb58f7bd Michael Hanselmann
2501 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2502 eb58f7bd Michael Hanselmann
    exitcode = None
2503 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2504 eb58f7bd Michael Hanselmann
  else:
2505 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2506 eb58f7bd Michael Hanselmann
    signum = None
2507 eb58f7bd Michael Hanselmann
2508 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2509 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2510 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2511 eb58f7bd Michael Hanselmann
2512 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2513 eb58f7bd Michael Hanselmann
2514 eb58f7bd Michael Hanselmann
2515 232144d0 Guido Trotter
def IgnoreSignals(fn, *args, **kwargs):
2516 232144d0 Guido Trotter
  """Tries to call a function ignoring failures due to EINTR.
2517 232144d0 Guido Trotter

2518 232144d0 Guido Trotter
  """
2519 232144d0 Guido Trotter
  try:
2520 232144d0 Guido Trotter
    return fn(*args, **kwargs)
2521 14aeab22 Guido Trotter
  except EnvironmentError, err:
2522 232144d0 Guido Trotter
    if err.errno != errno.EINTR:
2523 232144d0 Guido Trotter
      raise
2524 14aeab22 Guido Trotter
  except (select.error, socket.error), err:
2525 14aeab22 Guido Trotter
    # In python 2.6 and above select.error is an IOError, so it's handled
2526 14aeab22 Guido Trotter
    # above, in 2.5 and below it's not, and it's handled here.
2527 232144d0 Guido Trotter
    if not (err.args and err.args[0] == errno.EINTR):
2528 232144d0 Guido Trotter
      raise
2529 232144d0 Guido Trotter
2530 232144d0 Guido Trotter
2531 7996a135 Iustin Pop
def LockedMethod(fn):
2532 7996a135 Iustin Pop
  """Synchronized object access decorator.
2533 7996a135 Iustin Pop

2534 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2535 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2536 7996a135 Iustin Pop

2537 7996a135 Iustin Pop
  """
2538 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2539 e67bd559 Michael Hanselmann
    if debug_locks:
2540 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2541 e67bd559 Michael Hanselmann
2542 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2543 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2544 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2545 7996a135 Iustin Pop
    lock = self._lock
2546 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2547 7996a135 Iustin Pop
    lock.acquire()
2548 7996a135 Iustin Pop
    try:
2549 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2550 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2551 7996a135 Iustin Pop
    finally:
2552 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2553 7996a135 Iustin Pop
      lock.release()
2554 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2555 7996a135 Iustin Pop
    return result
2556 7996a135 Iustin Pop
  return wrapper
2557 eb0f0ce0 Michael Hanselmann
2558 eb0f0ce0 Michael Hanselmann
2559 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2560 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2561 eb0f0ce0 Michael Hanselmann

2562 58885d79 Iustin Pop
  @type fd: int
2563 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2564 58885d79 Iustin Pop

2565 eb0f0ce0 Michael Hanselmann
  """
2566 eb0f0ce0 Michael Hanselmann
  try:
2567 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2568 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2569 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2570 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2571 eb0f0ce0 Michael Hanselmann
    raise
2572 de499029 Michael Hanselmann
2573 de499029 Michael Hanselmann
2574 3b813dd2 Iustin Pop
def FormatTime(val):
2575 3b813dd2 Iustin Pop
  """Formats a time value.
2576 3b813dd2 Iustin Pop

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

2581 3b813dd2 Iustin Pop
  """
2582 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2583 3b813dd2 Iustin Pop
    return "N/A"
2584 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2585 3b813dd2 Iustin Pop
  # platforms
2586 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2587 3b813dd2 Iustin Pop
2588 3b813dd2 Iustin Pop
2589 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2590 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2591 05e50653 Michael Hanselmann

2592 5cbe43a5 Michael Hanselmann
  @type filename: string
2593 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2594 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2595 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2596 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2597 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2598 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2599 5cbe43a5 Michael Hanselmann

2600 05e50653 Michael Hanselmann
  """
2601 05e50653 Michael Hanselmann
  if now is None:
2602 05e50653 Michael Hanselmann
    now = time.time()
2603 05e50653 Michael Hanselmann
2604 05e50653 Michael Hanselmann
  try:
2605 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2606 05e50653 Michael Hanselmann
  except IOError, err:
2607 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2608 05e50653 Michael Hanselmann
      raise
2609 05e50653 Michael Hanselmann
    value = None
2610 05e50653 Michael Hanselmann
2611 05e50653 Michael Hanselmann
  if value is not None:
2612 05e50653 Michael Hanselmann
    try:
2613 05e50653 Michael Hanselmann
      value = int(value)
2614 05e50653 Michael Hanselmann
    except ValueError:
2615 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2616 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2617 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2618 05e50653 Michael Hanselmann
      value = None
2619 05e50653 Michael Hanselmann
2620 05e50653 Michael Hanselmann
    if value is not None:
2621 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2622 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2623 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2624 5cbe43a5 Michael Hanselmann
        value = None
2625 5cbe43a5 Michael Hanselmann
2626 5cbe43a5 Michael Hanselmann
      elif now > value:
2627 05e50653 Michael Hanselmann
        value = None
2628 05e50653 Michael Hanselmann
2629 05e50653 Michael Hanselmann
  return value
2630 05e50653 Michael Hanselmann
2631 05e50653 Michael Hanselmann
2632 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2633 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2634 de0ea66b Michael Hanselmann

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

2639 de0ea66b Michael Hanselmann
  """
2640 506be7c5 Guido Trotter
  def RaiseInner(self):
2641 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
2642 506be7c5 Guido Trotter
      raise self.args[0]
2643 506be7c5 Guido Trotter
    else:
2644 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
2645 de0ea66b Michael Hanselmann
2646 de0ea66b Michael Hanselmann
2647 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2648 de0ea66b Michael Hanselmann
  """Retry again.
2649 de0ea66b Michael Hanselmann

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

2654 de0ea66b Michael Hanselmann
  """
2655 de0ea66b Michael Hanselmann
2656 de0ea66b Michael Hanselmann
2657 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2658 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2659 de0ea66b Michael Hanselmann

2660 de0ea66b Michael Hanselmann
  """
2661 de0ea66b Michael Hanselmann
  __slots__ = [
2662 de0ea66b Michael Hanselmann
    "_factor",
2663 de0ea66b Michael Hanselmann
    "_limit",
2664 de0ea66b Michael Hanselmann
    "_next",
2665 de0ea66b Michael Hanselmann
    "_start",
2666 de0ea66b Michael Hanselmann
    ]
2667 de0ea66b Michael Hanselmann
2668 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2669 de0ea66b Michael Hanselmann
    """Initializes this class.
2670 de0ea66b Michael Hanselmann

2671 de0ea66b Michael Hanselmann
    @type start: float
2672 de0ea66b Michael Hanselmann
    @param start: Initial delay
2673 de0ea66b Michael Hanselmann
    @type factor: float
2674 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2675 de0ea66b Michael Hanselmann
    @type limit: float or None
2676 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2677 de0ea66b Michael Hanselmann

2678 de0ea66b Michael Hanselmann
    """
2679 de0ea66b Michael Hanselmann
    assert start > 0.0
2680 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2681 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2682 de0ea66b Michael Hanselmann
2683 de0ea66b Michael Hanselmann
    self._start = start
2684 de0ea66b Michael Hanselmann
    self._factor = factor
2685 de0ea66b Michael Hanselmann
    self._limit = limit
2686 de0ea66b Michael Hanselmann
2687 de0ea66b Michael Hanselmann
    self._next = start
2688 de0ea66b Michael Hanselmann
2689 de0ea66b Michael Hanselmann
  def __call__(self):
2690 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2691 de0ea66b Michael Hanselmann

2692 de0ea66b Michael Hanselmann
    """
2693 de0ea66b Michael Hanselmann
    current = self._next
2694 de0ea66b Michael Hanselmann
2695 de0ea66b Michael Hanselmann
    # Update for next run
2696 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2697 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2698 de0ea66b Michael Hanselmann
2699 de0ea66b Michael Hanselmann
    return current
2700 de0ea66b Michael Hanselmann
2701 de0ea66b Michael Hanselmann
2702 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2703 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2704 de0ea66b Michael Hanselmann
2705 de0ea66b Michael Hanselmann
2706 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2707 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2708 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2709 de0ea66b Michael Hanselmann

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

2714 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2715 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2716 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2717 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2718 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2719 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2720 de0ea66b Michael Hanselmann

2721 de0ea66b Michael Hanselmann
  @type fn: callable
2722 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2723 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2724 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2725 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2726 de0ea66b Michael Hanselmann
  @type timeout: float
2727 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2728 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2729 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2730 de0ea66b Michael Hanselmann
  @return: Return value of function
2731 de0ea66b Michael Hanselmann

2732 de0ea66b Michael Hanselmann
  """
2733 de0ea66b Michael Hanselmann
  assert callable(fn)
2734 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2735 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2736 de0ea66b Michael Hanselmann
2737 de0ea66b Michael Hanselmann
  if args is None:
2738 de0ea66b Michael Hanselmann
    args = []
2739 de0ea66b Michael Hanselmann
2740 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2741 de0ea66b Michael Hanselmann
2742 de0ea66b Michael Hanselmann
  if callable(delay):
2743 de0ea66b Michael Hanselmann
    # External function to calculate delay
2744 de0ea66b Michael Hanselmann
    calc_delay = delay
2745 de0ea66b Michael Hanselmann
2746 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2747 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2748 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2749 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2750 de0ea66b Michael Hanselmann
2751 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2752 de0ea66b Michael Hanselmann
    # Always use the remaining time
2753 de0ea66b Michael Hanselmann
    calc_delay = None
2754 de0ea66b Michael Hanselmann
2755 de0ea66b Michael Hanselmann
  else:
2756 de0ea66b Michael Hanselmann
    # Static delay
2757 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2758 de0ea66b Michael Hanselmann
2759 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2760 de0ea66b Michael Hanselmann
2761 de0ea66b Michael Hanselmann
  while True:
2762 506be7c5 Guido Trotter
    retry_args = []
2763 de0ea66b Michael Hanselmann
    try:
2764 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2765 de0ea66b Michael Hanselmann
      return fn(*args)
2766 506be7c5 Guido Trotter
    except RetryAgain, err:
2767 506be7c5 Guido Trotter
      retry_args = err.args
2768 1b429e2a Iustin Pop
    except RetryTimeout:
2769 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
2770 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
2771 de0ea66b Michael Hanselmann
2772 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2773 de0ea66b Michael Hanselmann
2774 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2775 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
2776 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
2777 de0ea66b Michael Hanselmann
2778 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2779 de0ea66b Michael Hanselmann
2780 de0ea66b Michael Hanselmann
    if calc_delay is None:
2781 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2782 de0ea66b Michael Hanselmann
    else:
2783 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2784 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2785 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2786 de0ea66b Michael Hanselmann
2787 de0ea66b Michael Hanselmann
2788 a87b4824 Michael Hanselmann
class FileLock(object):
2789 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2790 a87b4824 Michael Hanselmann

2791 a87b4824 Michael Hanselmann
  """
2792 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2793 58885d79 Iustin Pop
    """Constructor for FileLock.
2794 58885d79 Iustin Pop

2795 b4478d34 Michael Hanselmann
    @type fd: file
2796 b4478d34 Michael Hanselmann
    @param fd: File object
2797 58885d79 Iustin Pop
    @type filename: str
2798 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2799 58885d79 Iustin Pop

2800 58885d79 Iustin Pop
    """
2801 b4478d34 Michael Hanselmann
    self.fd = fd
2802 a87b4824 Michael Hanselmann
    self.filename = filename
2803 b4478d34 Michael Hanselmann
2804 b4478d34 Michael Hanselmann
  @classmethod
2805 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2806 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2807 b4478d34 Michael Hanselmann

2808 b4478d34 Michael Hanselmann
    @type filename: string
2809 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2810 b4478d34 Michael Hanselmann

2811 b4478d34 Michael Hanselmann
    """
2812 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2813 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2814 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2815 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2816 b4478d34 Michael Hanselmann
               filename)
2817 a87b4824 Michael Hanselmann
2818 a87b4824 Michael Hanselmann
  def __del__(self):
2819 a87b4824 Michael Hanselmann
    self.Close()
2820 a87b4824 Michael Hanselmann
2821 a87b4824 Michael Hanselmann
  def Close(self):
2822 58885d79 Iustin Pop
    """Close the file and release the lock.
2823 58885d79 Iustin Pop

2824 58885d79 Iustin Pop
    """
2825 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2826 a87b4824 Michael Hanselmann
      self.fd.close()
2827 a87b4824 Michael Hanselmann
      self.fd = None
2828 a87b4824 Michael Hanselmann
2829 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2830 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2831 aa74b828 Michael Hanselmann

2832 aa74b828 Michael Hanselmann
    @type flag: int
2833 58885d79 Iustin Pop
    @param flag: operation flag
2834 aa74b828 Michael Hanselmann
    @type blocking: bool
2835 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2836 aa74b828 Michael Hanselmann
    @type timeout: None or float
2837 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2838 aa74b828 Michael Hanselmann
                    non-blocking mode).
2839 aa74b828 Michael Hanselmann
    @type errmsg: string
2840 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2841 aa74b828 Michael Hanselmann

2842 aa74b828 Michael Hanselmann
    """
2843 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2844 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2845 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2846 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2847 a87b4824 Michael Hanselmann
2848 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2849 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2850 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2851 a87b4824 Michael Hanselmann
2852 cc4c9b91 Michael Hanselmann
    if timeout is None:
2853 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2854 cc4c9b91 Michael Hanselmann
    else:
2855 cc4c9b91 Michael Hanselmann
      try:
2856 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2857 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2858 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2859 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2860 aa74b828 Michael Hanselmann
2861 cc4c9b91 Michael Hanselmann
  @staticmethod
2862 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2863 cc4c9b91 Michael Hanselmann
    try:
2864 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2865 cc4c9b91 Michael Hanselmann
    except IOError, err:
2866 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2867 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2868 31892b4c Michael Hanselmann
2869 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2870 cc4c9b91 Michael Hanselmann
      raise
2871 aa74b828 Michael Hanselmann
2872 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2873 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2874 a87b4824 Michael Hanselmann

2875 58885d79 Iustin Pop
    @type blocking: boolean
2876 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2877 58885d79 Iustin Pop
        can lock the file or return immediately
2878 58885d79 Iustin Pop
    @type timeout: int or None
2879 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2880 58885d79 Iustin Pop
        (in blocking mode)
2881 58885d79 Iustin Pop

2882 a87b4824 Michael Hanselmann
    """
2883 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2884 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2885 a87b4824 Michael Hanselmann
2886 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2887 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2888 a87b4824 Michael Hanselmann

2889 58885d79 Iustin Pop
    @type blocking: boolean
2890 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2891 58885d79 Iustin Pop
        can lock the file or return immediately
2892 58885d79 Iustin Pop
    @type timeout: int or None
2893 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2894 58885d79 Iustin Pop
        (in blocking mode)
2895 58885d79 Iustin Pop

2896 a87b4824 Michael Hanselmann
    """
2897 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2898 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2899 a87b4824 Michael Hanselmann
2900 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2901 a87b4824 Michael Hanselmann
    """Unlocks the file.
2902 a87b4824 Michael Hanselmann

2903 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2904 58885d79 Iustin Pop
    operation::
2905 58885d79 Iustin Pop

2906 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2907 58885d79 Iustin Pop
      operations.
2908 58885d79 Iustin Pop

2909 58885d79 Iustin Pop
    @type blocking: boolean
2910 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2911 58885d79 Iustin Pop
        can lock the file or return immediately
2912 58885d79 Iustin Pop
    @type timeout: int or None
2913 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2914 58885d79 Iustin Pop
        (in blocking mode)
2915 a87b4824 Michael Hanselmann

2916 a87b4824 Michael Hanselmann
    """
2917 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2918 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2919 a87b4824 Michael Hanselmann
2920 a87b4824 Michael Hanselmann
2921 339be5a8 Michael Hanselmann
class LineSplitter:
2922 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
2923 339be5a8 Michael Hanselmann

2924 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
2925 339be5a8 Michael Hanselmann

2926 339be5a8 Michael Hanselmann
  """
2927 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
2928 339be5a8 Michael Hanselmann
    """Initializes this class.
2929 339be5a8 Michael Hanselmann

2930 339be5a8 Michael Hanselmann
    @type line_fn: callable
2931 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
2932 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
2933 339be5a8 Michael Hanselmann

2934 339be5a8 Michael Hanselmann
    """
2935 339be5a8 Michael Hanselmann
    assert callable(line_fn)
2936 339be5a8 Michael Hanselmann
2937 339be5a8 Michael Hanselmann
    if args:
2938 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
2939 339be5a8 Michael Hanselmann
      self._line_fn = \
2940 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
2941 339be5a8 Michael Hanselmann
    else:
2942 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
2943 339be5a8 Michael Hanselmann
2944 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
2945 339be5a8 Michael Hanselmann
    self._buffer = ""
2946 339be5a8 Michael Hanselmann
2947 339be5a8 Michael Hanselmann
  def write(self, data):
2948 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
2949 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
2950 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
2951 339be5a8 Michael Hanselmann
2952 339be5a8 Michael Hanselmann
  def flush(self):
2953 339be5a8 Michael Hanselmann
    while self._lines:
2954 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
2955 339be5a8 Michael Hanselmann
2956 339be5a8 Michael Hanselmann
  def close(self):
2957 339be5a8 Michael Hanselmann
    self.flush()
2958 339be5a8 Michael Hanselmann
    if self._buffer:
2959 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
2960 339be5a8 Michael Hanselmann
2961 339be5a8 Michael Hanselmann
2962 451575de Guido Trotter
def SignalHandled(signums):
2963 451575de Guido Trotter
  """Signal Handled decoration.
2964 451575de Guido Trotter

2965 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2966 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2967 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2968 451575de Guido Trotter
  objects as values.
2969 451575de Guido Trotter

2970 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2971 451575de Guido Trotter
  with different handlers.
2972 451575de Guido Trotter

2973 451575de Guido Trotter
  @type signums: list
2974 451575de Guido Trotter
  @param signums: signals to intercept
2975 451575de Guido Trotter

2976 451575de Guido Trotter
  """
2977 451575de Guido Trotter
  def wrap(fn):
2978 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2979 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2980 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2981 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2982 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2983 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2984 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2985 451575de Guido Trotter
      else:
2986 451575de Guido Trotter
        signal_handlers = {}
2987 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2988 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2989 451575de Guido Trotter
      try:
2990 451575de Guido Trotter
        for sig in signums:
2991 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2992 451575de Guido Trotter
        return fn(*args, **kwargs)
2993 451575de Guido Trotter
      finally:
2994 451575de Guido Trotter
        sighandler.Reset()
2995 451575de Guido Trotter
    return sig_function
2996 451575de Guido Trotter
  return wrap
2997 451575de Guido Trotter
2998 451575de Guido Trotter
2999 de499029 Michael Hanselmann
class SignalHandler(object):
3000 de499029 Michael Hanselmann
  """Generic signal handler class.
3001 de499029 Michael Hanselmann

3002 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
3003 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
3004 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
3005 58885d79 Iustin Pop
  signal was sent.
3006 58885d79 Iustin Pop

3007 58885d79 Iustin Pop
  @type signum: list
3008 58885d79 Iustin Pop
  @ivar signum: the signals we handle
3009 58885d79 Iustin Pop
  @type called: boolean
3010 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
3011 de499029 Michael Hanselmann

3012 de499029 Michael Hanselmann
  """
3013 de499029 Michael Hanselmann
  def __init__(self, signum):
3014 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
3015 de499029 Michael Hanselmann

3016 58885d79 Iustin Pop
    @type signum: int or list of ints
3017 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
3018 de499029 Michael Hanselmann

3019 de499029 Michael Hanselmann
    """
3020 6c52849e Guido Trotter
    self.signum = set(signum)
3021 de499029 Michael Hanselmann
    self.called = False
3022 de499029 Michael Hanselmann
3023 de499029 Michael Hanselmann
    self._previous = {}
3024 de499029 Michael Hanselmann
    try:
3025 de499029 Michael Hanselmann
      for signum in self.signum:
3026 de499029 Michael Hanselmann
        # Setup handler
3027 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3028 de499029 Michael Hanselmann
        try:
3029 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3030 de499029 Michael Hanselmann
        except:
3031 de499029 Michael Hanselmann
          # Restore previous handler
3032 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3033 de499029 Michael Hanselmann
          raise
3034 de499029 Michael Hanselmann
    except:
3035 de499029 Michael Hanselmann
      # Reset all handlers
3036 de499029 Michael Hanselmann
      self.Reset()
3037 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3038 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3039 de499029 Michael Hanselmann
      raise
3040 de499029 Michael Hanselmann
3041 de499029 Michael Hanselmann
  def __del__(self):
3042 de499029 Michael Hanselmann
    self.Reset()
3043 de499029 Michael Hanselmann
3044 de499029 Michael Hanselmann
  def Reset(self):
3045 de499029 Michael Hanselmann
    """Restore previous handler.
3046 de499029 Michael Hanselmann

3047 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3048 58885d79 Iustin Pop

3049 de499029 Michael Hanselmann
    """
3050 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3051 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3052 de499029 Michael Hanselmann
      # If successful, remove from dict
3053 de499029 Michael Hanselmann
      del self._previous[signum]
3054 de499029 Michael Hanselmann
3055 de499029 Michael Hanselmann
  def Clear(self):
3056 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3057 de499029 Michael Hanselmann

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

3060 de499029 Michael Hanselmann
    """
3061 de499029 Michael Hanselmann
    self.called = False
3062 de499029 Michael Hanselmann
3063 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
3064 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
3065 de499029 Michael Hanselmann
    """Actual signal handling function.
3066 de499029 Michael Hanselmann

3067 de499029 Michael Hanselmann
    """
3068 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3069 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3070 de499029 Michael Hanselmann
    self.called = True
3071 a2d2e1a7 Iustin Pop
3072 a2d2e1a7 Iustin Pop
3073 a2d2e1a7 Iustin Pop
class FieldSet(object):
3074 a2d2e1a7 Iustin Pop
  """A simple field set.
3075 a2d2e1a7 Iustin Pop

3076 a2d2e1a7 Iustin Pop
  Among the features are:
3077 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3078 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3079 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3080 a2d2e1a7 Iustin Pop

3081 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3082 a2d2e1a7 Iustin Pop

3083 a2d2e1a7 Iustin Pop
  """
3084 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3085 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3086 a2d2e1a7 Iustin Pop
3087 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3088 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3089 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3090 a2d2e1a7 Iustin Pop
3091 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3092 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3093 a2d2e1a7 Iustin Pop

3094 a2d2e1a7 Iustin Pop
    @type field: str
3095 a2d2e1a7 Iustin Pop
    @param field: the string to match
3096 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3097 a2d2e1a7 Iustin Pop

3098 a2d2e1a7 Iustin Pop
    """
3099 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3100 a2d2e1a7 Iustin Pop
      return m
3101 6c881c52 Iustin Pop
    return None
3102 a2d2e1a7 Iustin Pop
3103 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3104 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3105 a2d2e1a7 Iustin Pop

3106 a2d2e1a7 Iustin Pop
    @type items: list
3107 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3108 a2d2e1a7 Iustin Pop
    @rtype: list
3109 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3110 a2d2e1a7 Iustin Pop

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