Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 05b35f15

History | View | Annotate | Download (83.8 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 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1392 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1393 9dae41ad Guido Trotter
1394 9dae41ad Guido Trotter
1395 582ed043 Guido Trotter
def ReadFile(file_name, size=-1):
1396 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1397 ca0aa6d0 Michael Hanselmann

1398 016308cb Iustin Pop
  @type size: int
1399 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1400 58885d79 Iustin Pop
  @rtype: str
1401 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1402 ca0aa6d0 Michael Hanselmann

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

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

1424 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1425 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1426 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1427 087b34fe Iustin Pop
  temporary file should be removed.
1428 087b34fe Iustin Pop

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

1453 58885d79 Iustin Pop
  @rtype: None or int
1454 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1455 58885d79 Iustin Pop
      otherwise the file descriptor
1456 58885d79 Iustin Pop

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

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

1513 e587b46a Guido Trotter
  @type strict: boolean
1514 e587b46a Guido Trotter
  @param strict: if True, abort if the file has more than one
1515 e587b46a Guido Trotter
      non-empty line
1516 e587b46a Guido Trotter

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

1531 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1532 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1533 7b4126b7 Iustin Pop
  value, the index will be returned.
1534 7b4126b7 Iustin Pop

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

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

1540 58885d79 Iustin Pop
  @type seq: sequence
1541 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1542 58885d79 Iustin Pop
  @type base: int
1543 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1544 58885d79 Iustin Pop
  @rtype: int
1545 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1546 7b4126b7 Iustin Pop

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

1559 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
1560 dfdc4060 Guido Trotter

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

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

1598 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
1599 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
1600 dfdc4060 Guido Trotter
  expired.
1601 dfdc4060 Guido Trotter

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

1621 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
1622 dfdc4060 Guido Trotter

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

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

1650 f7414041 Michael Hanselmann
  Element order is preserved.
1651 58885d79 Iustin Pop

1652 58885d79 Iustin Pop
  @type seq: sequence
1653 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1654 58885d79 Iustin Pop
  @rtype: list
1655 58885d79 Iustin Pop
  @return: list of unique elements from seq
1656 58885d79 Iustin Pop

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

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

1668 58885d79 Iustin Pop
  @type mac: str
1669 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1670 82187135 René Nussbaumer
  @rtype: str
1671 82187135 René Nussbaumer
  @return: returns the normalized and validated MAC.
1672 82187135 René Nussbaumer

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

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

1687 58885d79 Iustin Pop
  @type duration: float
1688 58885d79 Iustin Pop
  @param duration: the sleep duration
1689 58885d79 Iustin Pop
  @rtype: boolean
1690 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1691 58885d79 Iustin Pop

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

1702 7d88772a Iustin Pop
  @type fd: int
1703 7d88772a Iustin Pop
  @param fd: the file descriptor
1704 7d88772a Iustin Pop
  @type retries: int
1705 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1706 7d88772a Iustin Pop
      other error than EBADF
1707 7d88772a Iustin Pop

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

1722 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1723 7d88772a Iustin Pop
  stdin/out/err).
1724 8f765069 Iustin Pop

1725 58885d79 Iustin Pop
  @type noclose_fds: list or None
1726 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1727 58885d79 Iustin Pop
      that should not be closed
1728 58885d79 Iustin Pop

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

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

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

1785 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1786 7d88772a Iustin Pop
  runs it in the background as a daemon.
1787 7d88772a Iustin Pop

1788 7d88772a Iustin Pop
  @type logfile: str
1789 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1790 7d88772a Iustin Pop
  @rtype: int
1791 5fcc718f Iustin Pop
  @return: the value zero
1792 7d88772a Iustin Pop

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

1828 58885d79 Iustin Pop
  @type name: str
1829 58885d79 Iustin Pop
  @param name: the daemon name
1830 58885d79 Iustin Pop
  @rtype: str
1831 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1832 58885d79 Iustin Pop
      daemon name
1833 b330ac0b Guido Trotter

1834 b330ac0b Guido Trotter
  """
1835 c4feafe8 Iustin Pop
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1836 b330ac0b Guido Trotter
1837 b330ac0b Guido Trotter
1838 2826b361 Guido Trotter
def EnsureDaemon(name):
1839 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
1840 2826b361 Guido Trotter

1841 2826b361 Guido Trotter
  """
1842 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1843 2826b361 Guido Trotter
  if result.failed:
1844 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1845 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
1846 2826b361 Guido Trotter
    return False
1847 2826b361 Guido Trotter
1848 2826b361 Guido Trotter
  return True
1849 2826b361 Guido Trotter
1850 2826b361 Guido Trotter
1851 b330ac0b Guido Trotter
def WritePidFile(name):
1852 b330ac0b Guido Trotter
  """Write the current process pidfile.
1853 b330ac0b Guido Trotter

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

1856 58885d79 Iustin Pop
  @type name: str
1857 58885d79 Iustin Pop
  @param name: the daemon name to use
1858 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1859 58885d79 Iustin Pop
      points to a live process
1860 b330ac0b Guido Trotter

1861 b330ac0b Guido Trotter
  """
1862 b330ac0b Guido Trotter
  pid = os.getpid()
1863 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1864 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1865 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1866 b330ac0b Guido Trotter
1867 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1868 b330ac0b Guido Trotter
1869 b330ac0b Guido Trotter
1870 b330ac0b Guido Trotter
def RemovePidFile(name):
1871 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1872 b330ac0b Guido Trotter

1873 b330ac0b Guido Trotter
  Any errors are ignored.
1874 b330ac0b Guido Trotter

1875 58885d79 Iustin Pop
  @type name: str
1876 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1877 58885d79 Iustin Pop

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

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

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

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

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

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

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

1991 58885d79 Iustin Pop
  @type vglist: dict
1992 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1993 58885d79 Iustin Pop
  @type vgname: str
1994 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1995 58885d79 Iustin Pop
  @type minsize: int
1996 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1997 58885d79 Iustin Pop
  @rtype: None or str
1998 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1999 8d1a2a64 Michael Hanselmann

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

2013 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
2014 45bc5e4a Michael Hanselmann
  @type value: int or float
2015 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
2016 739be818 Michael Hanselmann

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

2031 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
2032 739be818 Michael Hanselmann
  @type timetuple: tuple
2033 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
2034 739be818 Michael Hanselmann

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

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

2053 cd50653c Guido Trotter
  @type daemon_name: string
2054 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2055 58885d79 Iustin Pop
  @rtype: int
2056 58885d79 Iustin Pop

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

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

2077 de3b8e39 Luca Bigliardi
  """
2078 de3b8e39 Luca Bigliardi
  def __init__(self, filename, mode="a", encoding=None):
2079 de3b8e39 Luca Bigliardi
    """Open the specified file and use it as the stream for logging.
2080 de3b8e39 Luca Bigliardi

2081 de3b8e39 Luca Bigliardi
    Also open /dev/console to report errors while logging.
2082 de3b8e39 Luca Bigliardi

2083 de3b8e39 Luca Bigliardi
    """
2084 de3b8e39 Luca Bigliardi
    logging.FileHandler.__init__(self, filename, mode, encoding)
2085 de3b8e39 Luca Bigliardi
    self.console = open(constants.DEV_CONSOLE, "a")
2086 de3b8e39 Luca Bigliardi
2087 de3b8e39 Luca Bigliardi
  def handleError(self, record):
2088 de3b8e39 Luca Bigliardi
    """Handle errors which occur during an emit() call.
2089 de3b8e39 Luca Bigliardi

2090 de3b8e39 Luca Bigliardi
    Try to handle errors with FileHandler method, if it fails write to
2091 de3b8e39 Luca Bigliardi
    /dev/console.
2092 de3b8e39 Luca Bigliardi

2093 de3b8e39 Luca Bigliardi
    """
2094 de3b8e39 Luca Bigliardi
    try:
2095 05b35f15 Luca Bigliardi
      logging.FileHandler.handleError(self, record)
2096 de3b8e39 Luca Bigliardi
    except Exception:
2097 de3b8e39 Luca Bigliardi
      try:
2098 de3b8e39 Luca Bigliardi
        self.console.write("Cannot log message:\n%s\n" % self.format(record))
2099 de3b8e39 Luca Bigliardi
      except Exception:
2100 de3b8e39 Luca Bigliardi
        # Log handler tried everything it could, now just give up
2101 de3b8e39 Luca Bigliardi
        pass
2102 de3b8e39 Luca Bigliardi
2103 de3b8e39 Luca Bigliardi
2104 ea34193f Iustin Pop
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2105 49e60a28 Luca Bigliardi
                 multithreaded=False, syslog=constants.SYSLOG_USAGE,
2106 49e60a28 Luca Bigliardi
                 console_logging=False):
2107 82d9caef Iustin Pop
  """Configures the logging module.
2108 82d9caef Iustin Pop

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

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

2200 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
2201 da961187 Guido Trotter

2202 da961187 Guido Trotter
  """
2203 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
2204 82d9caef Iustin Pop
2205 016d04b3 Michael Hanselmann
2206 4bb678e9 Iustin Pop
def PathJoin(*args):
2207 4bb678e9 Iustin Pop
  """Safe-join a list of path components.
2208 4bb678e9 Iustin Pop

2209 4bb678e9 Iustin Pop
  Requirements:
2210 4bb678e9 Iustin Pop
      - the first argument must be an absolute path
2211 4bb678e9 Iustin Pop
      - no component in the path must have backtracking (e.g. /../),
2212 4bb678e9 Iustin Pop
        since we check for normalization at the end
2213 4bb678e9 Iustin Pop

2214 4bb678e9 Iustin Pop
  @param args: the path components to be joined
2215 4bb678e9 Iustin Pop
  @raise ValueError: for invalid paths
2216 4bb678e9 Iustin Pop

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

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

2243 f65f63ef Iustin Pop
  @param fname: the file name
2244 f65f63ef Iustin Pop
  @type lines: int
2245 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
2246 f65f63ef Iustin Pop

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

2265 27e46076 Michael Hanselmann
  @type value: string
2266 27e46076 Michael Hanselmann
  @param value: ASN1 GENERALIZEDTIME timestamp
2267 27e46076 Michael Hanselmann

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

2292 27e46076 Michael Hanselmann
  @type cert: OpenSSL.crypto.X509
2293 27e46076 Michael Hanselmann
  @param cert: X509 certificate object
2294 27e46076 Michael Hanselmann

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

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

2337 26f15862 Iustin Pop
  @type text: str or unicode
2338 26f15862 Iustin Pop
  @param text: input data
2339 26f15862 Iustin Pop
  @rtype: str
2340 26f15862 Iustin Pop
  @return: a safe version of text
2341 26f15862 Iustin Pop

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

2365 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
2366 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
2367 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
2368 5b69bc7c Iustin Pop
  separator):
2369 5b69bc7c Iustin Pop
    - a plain , separates the elements
2370 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
2371 5b69bc7c Iustin Pop
      backslash plus a separator comma
2372 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
2373 5b69bc7c Iustin Pop
      non-separator comma
2374 5b69bc7c Iustin Pop

2375 5b69bc7c Iustin Pop
  @type text: string
2376 5b69bc7c Iustin Pop
  @param text: the string to split
2377 5b69bc7c Iustin Pop
  @type sep: string
2378 5b69bc7c Iustin Pop
  @param text: the separator
2379 5b69bc7c Iustin Pop
  @rtype: string
2380 5b69bc7c Iustin Pop
  @return: a list of strings
2381 5b69bc7c Iustin Pop

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

2407 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
2408 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
2409 ab3e6da8 Iustin Pop

2410 ab3e6da8 Iustin Pop
  """
2411 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
2412 ab3e6da8 Iustin Pop
2413 ab3e6da8 Iustin Pop
2414 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
2415 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
2416 3f6a47a8 Michael Hanselmann

2417 3f6a47a8 Michael Hanselmann
  @type value: int
2418 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
2419 3f6a47a8 Michael Hanselmann
  @rtype: int
2420 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
2421 3f6a47a8 Michael Hanselmann

2422 3f6a47a8 Michael Hanselmann
  """
2423 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
2424 3f6a47a8 Michael Hanselmann
2425 3f6a47a8 Michael Hanselmann
2426 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
2427 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
2428 3f6a47a8 Michael Hanselmann

2429 3f6a47a8 Michael Hanselmann
  @type path: string
2430 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
2431 3f6a47a8 Michael Hanselmann
  @rtype: int
2432 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
2433 3f6a47a8 Michael Hanselmann

2434 3f6a47a8 Michael Hanselmann
  """
2435 3f6a47a8 Michael Hanselmann
  size = 0
2436 3f6a47a8 Michael Hanselmann
2437 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
2438 2a887df9 Michael Hanselmann
    for filename in files:
2439 c4feafe8 Iustin Pop
      st = os.lstat(PathJoin(curpath, filename))
2440 3f6a47a8 Michael Hanselmann
      size += st.st_size
2441 3f6a47a8 Michael Hanselmann
2442 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
2443 3f6a47a8 Michael Hanselmann
2444 3f6a47a8 Michael Hanselmann
2445 620a85fd Iustin Pop
def GetFilesystemStats(path):
2446 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
2447 3f6a47a8 Michael Hanselmann

2448 3f6a47a8 Michael Hanselmann
  @type path: string
2449 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
2450 3f6a47a8 Michael Hanselmann
  @rtype: int
2451 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
2452 3f6a47a8 Michael Hanselmann

2453 3f6a47a8 Michael Hanselmann
  """
2454 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
2455 3f6a47a8 Michael Hanselmann
2456 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2457 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2458 620a85fd Iustin Pop
  return (tsize, fsize)
2459 3f6a47a8 Michael Hanselmann
2460 3f6a47a8 Michael Hanselmann
2461 bdefe5dd Michael Hanselmann
def RunInSeparateProcess(fn, *args):
2462 eb58f7bd Michael Hanselmann
  """Runs a function in a separate process.
2463 eb58f7bd Michael Hanselmann

2464 eb58f7bd Michael Hanselmann
  Note: Only boolean return values are supported.
2465 eb58f7bd Michael Hanselmann

2466 eb58f7bd Michael Hanselmann
  @type fn: callable
2467 eb58f7bd Michael Hanselmann
  @param fn: Function to be called
2468 bdefe5dd Michael Hanselmann
  @rtype: bool
2469 bdefe5dd Michael Hanselmann
  @return: Function's result
2470 eb58f7bd Michael Hanselmann

2471 eb58f7bd Michael Hanselmann
  """
2472 eb58f7bd Michael Hanselmann
  pid = os.fork()
2473 eb58f7bd Michael Hanselmann
  if pid == 0:
2474 eb58f7bd Michael Hanselmann
    # Child process
2475 eb58f7bd Michael Hanselmann
    try:
2476 82869978 Michael Hanselmann
      # In case the function uses temporary files
2477 82869978 Michael Hanselmann
      ResetTempfileModule()
2478 82869978 Michael Hanselmann
2479 eb58f7bd Michael Hanselmann
      # Call function
2480 bdefe5dd Michael Hanselmann
      result = int(bool(fn(*args)))
2481 eb58f7bd Michael Hanselmann
      assert result in (0, 1)
2482 eb58f7bd Michael Hanselmann
    except: # pylint: disable-msg=W0702
2483 eb58f7bd Michael Hanselmann
      logging.exception("Error while calling function in separate process")
2484 eb58f7bd Michael Hanselmann
      # 0 and 1 are reserved for the return value
2485 eb58f7bd Michael Hanselmann
      result = 33
2486 eb58f7bd Michael Hanselmann
2487 eb58f7bd Michael Hanselmann
    os._exit(result) # pylint: disable-msg=W0212
2488 eb58f7bd Michael Hanselmann
2489 eb58f7bd Michael Hanselmann
  # Parent process
2490 eb58f7bd Michael Hanselmann
2491 eb58f7bd Michael Hanselmann
  # Avoid zombies and check exit code
2492 eb58f7bd Michael Hanselmann
  (_, status) = os.waitpid(pid, 0)
2493 eb58f7bd Michael Hanselmann
2494 eb58f7bd Michael Hanselmann
  if os.WIFSIGNALED(status):
2495 eb58f7bd Michael Hanselmann
    exitcode = None
2496 eb58f7bd Michael Hanselmann
    signum = os.WTERMSIG(status)
2497 eb58f7bd Michael Hanselmann
  else:
2498 eb58f7bd Michael Hanselmann
    exitcode = os.WEXITSTATUS(status)
2499 eb58f7bd Michael Hanselmann
    signum = None
2500 eb58f7bd Michael Hanselmann
2501 eb58f7bd Michael Hanselmann
  if not (exitcode in (0, 1) and signum is None):
2502 eb58f7bd Michael Hanselmann
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2503 eb58f7bd Michael Hanselmann
                              (exitcode, signum))
2504 eb58f7bd Michael Hanselmann
2505 eb58f7bd Michael Hanselmann
  return bool(exitcode)
2506 eb58f7bd Michael Hanselmann
2507 eb58f7bd Michael Hanselmann
2508 7996a135 Iustin Pop
def LockedMethod(fn):
2509 7996a135 Iustin Pop
  """Synchronized object access decorator.
2510 7996a135 Iustin Pop

2511 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2512 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2513 7996a135 Iustin Pop

2514 7996a135 Iustin Pop
  """
2515 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2516 e67bd559 Michael Hanselmann
    if debug_locks:
2517 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2518 e67bd559 Michael Hanselmann
2519 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2520 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2521 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2522 7996a135 Iustin Pop
    lock = self._lock
2523 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2524 7996a135 Iustin Pop
    lock.acquire()
2525 7996a135 Iustin Pop
    try:
2526 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2527 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2528 7996a135 Iustin Pop
    finally:
2529 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2530 7996a135 Iustin Pop
      lock.release()
2531 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2532 7996a135 Iustin Pop
    return result
2533 7996a135 Iustin Pop
  return wrapper
2534 eb0f0ce0 Michael Hanselmann
2535 eb0f0ce0 Michael Hanselmann
2536 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2537 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2538 eb0f0ce0 Michael Hanselmann

2539 58885d79 Iustin Pop
  @type fd: int
2540 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2541 58885d79 Iustin Pop

2542 eb0f0ce0 Michael Hanselmann
  """
2543 eb0f0ce0 Michael Hanselmann
  try:
2544 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2545 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2546 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2547 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2548 eb0f0ce0 Michael Hanselmann
    raise
2549 de499029 Michael Hanselmann
2550 de499029 Michael Hanselmann
2551 3b813dd2 Iustin Pop
def FormatTime(val):
2552 3b813dd2 Iustin Pop
  """Formats a time value.
2553 3b813dd2 Iustin Pop

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

2558 3b813dd2 Iustin Pop
  """
2559 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2560 3b813dd2 Iustin Pop
    return "N/A"
2561 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2562 3b813dd2 Iustin Pop
  # platforms
2563 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2564 3b813dd2 Iustin Pop
2565 3b813dd2 Iustin Pop
2566 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2567 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2568 05e50653 Michael Hanselmann

2569 5cbe43a5 Michael Hanselmann
  @type filename: string
2570 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2571 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2572 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2573 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2574 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2575 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2576 5cbe43a5 Michael Hanselmann

2577 05e50653 Michael Hanselmann
  """
2578 05e50653 Michael Hanselmann
  if now is None:
2579 05e50653 Michael Hanselmann
    now = time.time()
2580 05e50653 Michael Hanselmann
2581 05e50653 Michael Hanselmann
  try:
2582 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2583 05e50653 Michael Hanselmann
  except IOError, err:
2584 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2585 05e50653 Michael Hanselmann
      raise
2586 05e50653 Michael Hanselmann
    value = None
2587 05e50653 Michael Hanselmann
2588 05e50653 Michael Hanselmann
  if value is not None:
2589 05e50653 Michael Hanselmann
    try:
2590 05e50653 Michael Hanselmann
      value = int(value)
2591 05e50653 Michael Hanselmann
    except ValueError:
2592 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2593 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2594 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2595 05e50653 Michael Hanselmann
      value = None
2596 05e50653 Michael Hanselmann
2597 05e50653 Michael Hanselmann
    if value is not None:
2598 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2599 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2600 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2601 5cbe43a5 Michael Hanselmann
        value = None
2602 5cbe43a5 Michael Hanselmann
2603 5cbe43a5 Michael Hanselmann
      elif now > value:
2604 05e50653 Michael Hanselmann
        value = None
2605 05e50653 Michael Hanselmann
2606 05e50653 Michael Hanselmann
  return value
2607 05e50653 Michael Hanselmann
2608 05e50653 Michael Hanselmann
2609 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2610 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2611 de0ea66b Michael Hanselmann

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

2616 de0ea66b Michael Hanselmann
  """
2617 506be7c5 Guido Trotter
  def RaiseInner(self):
2618 506be7c5 Guido Trotter
    if self.args and isinstance(self.args[0], Exception):
2619 506be7c5 Guido Trotter
      raise self.args[0]
2620 506be7c5 Guido Trotter
    else:
2621 506be7c5 Guido Trotter
      raise RetryTimeout(*self.args)
2622 de0ea66b Michael Hanselmann
2623 de0ea66b Michael Hanselmann
2624 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2625 de0ea66b Michael Hanselmann
  """Retry again.
2626 de0ea66b Michael Hanselmann

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

2631 de0ea66b Michael Hanselmann
  """
2632 de0ea66b Michael Hanselmann
2633 de0ea66b Michael Hanselmann
2634 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2635 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2636 de0ea66b Michael Hanselmann

2637 de0ea66b Michael Hanselmann
  """
2638 de0ea66b Michael Hanselmann
  __slots__ = [
2639 de0ea66b Michael Hanselmann
    "_factor",
2640 de0ea66b Michael Hanselmann
    "_limit",
2641 de0ea66b Michael Hanselmann
    "_next",
2642 de0ea66b Michael Hanselmann
    "_start",
2643 de0ea66b Michael Hanselmann
    ]
2644 de0ea66b Michael Hanselmann
2645 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2646 de0ea66b Michael Hanselmann
    """Initializes this class.
2647 de0ea66b Michael Hanselmann

2648 de0ea66b Michael Hanselmann
    @type start: float
2649 de0ea66b Michael Hanselmann
    @param start: Initial delay
2650 de0ea66b Michael Hanselmann
    @type factor: float
2651 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2652 de0ea66b Michael Hanselmann
    @type limit: float or None
2653 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2654 de0ea66b Michael Hanselmann

2655 de0ea66b Michael Hanselmann
    """
2656 de0ea66b Michael Hanselmann
    assert start > 0.0
2657 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2658 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2659 de0ea66b Michael Hanselmann
2660 de0ea66b Michael Hanselmann
    self._start = start
2661 de0ea66b Michael Hanselmann
    self._factor = factor
2662 de0ea66b Michael Hanselmann
    self._limit = limit
2663 de0ea66b Michael Hanselmann
2664 de0ea66b Michael Hanselmann
    self._next = start
2665 de0ea66b Michael Hanselmann
2666 de0ea66b Michael Hanselmann
  def __call__(self):
2667 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2668 de0ea66b Michael Hanselmann

2669 de0ea66b Michael Hanselmann
    """
2670 de0ea66b Michael Hanselmann
    current = self._next
2671 de0ea66b Michael Hanselmann
2672 de0ea66b Michael Hanselmann
    # Update for next run
2673 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2674 26751075 Michael Hanselmann
      self._next = min(self._limit, self._next * self._factor)
2675 de0ea66b Michael Hanselmann
2676 de0ea66b Michael Hanselmann
    return current
2677 de0ea66b Michael Hanselmann
2678 de0ea66b Michael Hanselmann
2679 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2680 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2681 de0ea66b Michael Hanselmann
2682 de0ea66b Michael Hanselmann
2683 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2684 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2685 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2686 de0ea66b Michael Hanselmann

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

2691 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2692 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2693 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2694 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2695 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2696 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2697 de0ea66b Michael Hanselmann

2698 de0ea66b Michael Hanselmann
  @type fn: callable
2699 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2700 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2701 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2702 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2703 de0ea66b Michael Hanselmann
  @type timeout: float
2704 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2705 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2706 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2707 de0ea66b Michael Hanselmann
  @return: Return value of function
2708 de0ea66b Michael Hanselmann

2709 de0ea66b Michael Hanselmann
  """
2710 de0ea66b Michael Hanselmann
  assert callable(fn)
2711 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2712 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2713 de0ea66b Michael Hanselmann
2714 de0ea66b Michael Hanselmann
  if args is None:
2715 de0ea66b Michael Hanselmann
    args = []
2716 de0ea66b Michael Hanselmann
2717 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2718 de0ea66b Michael Hanselmann
2719 de0ea66b Michael Hanselmann
  if callable(delay):
2720 de0ea66b Michael Hanselmann
    # External function to calculate delay
2721 de0ea66b Michael Hanselmann
    calc_delay = delay
2722 de0ea66b Michael Hanselmann
2723 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2724 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2725 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2726 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2727 de0ea66b Michael Hanselmann
2728 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2729 de0ea66b Michael Hanselmann
    # Always use the remaining time
2730 de0ea66b Michael Hanselmann
    calc_delay = None
2731 de0ea66b Michael Hanselmann
2732 de0ea66b Michael Hanselmann
  else:
2733 de0ea66b Michael Hanselmann
    # Static delay
2734 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2735 de0ea66b Michael Hanselmann
2736 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2737 de0ea66b Michael Hanselmann
2738 de0ea66b Michael Hanselmann
  while True:
2739 506be7c5 Guido Trotter
    retry_args = []
2740 de0ea66b Michael Hanselmann
    try:
2741 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2742 de0ea66b Michael Hanselmann
      return fn(*args)
2743 506be7c5 Guido Trotter
    except RetryAgain, err:
2744 506be7c5 Guido Trotter
      retry_args = err.args
2745 1b429e2a Iustin Pop
    except RetryTimeout:
2746 1b429e2a Iustin Pop
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
2747 1b429e2a Iustin Pop
                                   " handle RetryTimeout")
2748 de0ea66b Michael Hanselmann
2749 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2750 de0ea66b Michael Hanselmann
2751 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2752 506be7c5 Guido Trotter
      # pylint: disable-msg=W0142
2753 506be7c5 Guido Trotter
      raise RetryTimeout(*retry_args)
2754 de0ea66b Michael Hanselmann
2755 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2756 de0ea66b Michael Hanselmann
2757 de0ea66b Michael Hanselmann
    if calc_delay is None:
2758 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2759 de0ea66b Michael Hanselmann
    else:
2760 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2761 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2762 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2763 de0ea66b Michael Hanselmann
2764 de0ea66b Michael Hanselmann
2765 a87b4824 Michael Hanselmann
class FileLock(object):
2766 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2767 a87b4824 Michael Hanselmann

2768 a87b4824 Michael Hanselmann
  """
2769 b4478d34 Michael Hanselmann
  def __init__(self, fd, filename):
2770 58885d79 Iustin Pop
    """Constructor for FileLock.
2771 58885d79 Iustin Pop

2772 b4478d34 Michael Hanselmann
    @type fd: file
2773 b4478d34 Michael Hanselmann
    @param fd: File object
2774 58885d79 Iustin Pop
    @type filename: str
2775 b4478d34 Michael Hanselmann
    @param filename: Path of the file opened at I{fd}
2776 58885d79 Iustin Pop

2777 58885d79 Iustin Pop
    """
2778 b4478d34 Michael Hanselmann
    self.fd = fd
2779 a87b4824 Michael Hanselmann
    self.filename = filename
2780 b4478d34 Michael Hanselmann
2781 b4478d34 Michael Hanselmann
  @classmethod
2782 b4478d34 Michael Hanselmann
  def Open(cls, filename):
2783 b4478d34 Michael Hanselmann
    """Creates and opens a file to be used as a file-based lock.
2784 b4478d34 Michael Hanselmann

2785 b4478d34 Michael Hanselmann
    @type filename: string
2786 b4478d34 Michael Hanselmann
    @param filename: path to the file to be locked
2787 b4478d34 Michael Hanselmann

2788 b4478d34 Michael Hanselmann
    """
2789 b4478d34 Michael Hanselmann
    # Using "os.open" is necessary to allow both opening existing file
2790 b4478d34 Michael Hanselmann
    # read/write and creating if not existing. Vanilla "open" will truncate an
2791 b4478d34 Michael Hanselmann
    # existing file -or- allow creating if not existing.
2792 b4478d34 Michael Hanselmann
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2793 b4478d34 Michael Hanselmann
               filename)
2794 a87b4824 Michael Hanselmann
2795 a87b4824 Michael Hanselmann
  def __del__(self):
2796 a87b4824 Michael Hanselmann
    self.Close()
2797 a87b4824 Michael Hanselmann
2798 a87b4824 Michael Hanselmann
  def Close(self):
2799 58885d79 Iustin Pop
    """Close the file and release the lock.
2800 58885d79 Iustin Pop

2801 58885d79 Iustin Pop
    """
2802 aac3fbf0 Iustin Pop
    if hasattr(self, "fd") and self.fd:
2803 a87b4824 Michael Hanselmann
      self.fd.close()
2804 a87b4824 Michael Hanselmann
      self.fd = None
2805 a87b4824 Michael Hanselmann
2806 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2807 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2808 aa74b828 Michael Hanselmann

2809 aa74b828 Michael Hanselmann
    @type flag: int
2810 58885d79 Iustin Pop
    @param flag: operation flag
2811 aa74b828 Michael Hanselmann
    @type blocking: bool
2812 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2813 aa74b828 Michael Hanselmann
    @type timeout: None or float
2814 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2815 aa74b828 Michael Hanselmann
                    non-blocking mode).
2816 aa74b828 Michael Hanselmann
    @type errmsg: string
2817 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2818 aa74b828 Michael Hanselmann

2819 aa74b828 Michael Hanselmann
    """
2820 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2821 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2822 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2823 cc4c9b91 Michael Hanselmann
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2824 a87b4824 Michael Hanselmann
2825 cc4c9b91 Michael Hanselmann
    # When a timeout is used, LOCK_NB must always be set
2826 cc4c9b91 Michael Hanselmann
    if not (timeout is None and blocking):
2827 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2828 a87b4824 Michael Hanselmann
2829 cc4c9b91 Michael Hanselmann
    if timeout is None:
2830 cc4c9b91 Michael Hanselmann
      self._Lock(self.fd, flag, timeout)
2831 cc4c9b91 Michael Hanselmann
    else:
2832 cc4c9b91 Michael Hanselmann
      try:
2833 cc4c9b91 Michael Hanselmann
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2834 cc4c9b91 Michael Hanselmann
              args=(self.fd, flag, timeout))
2835 cc4c9b91 Michael Hanselmann
      except RetryTimeout:
2836 cc4c9b91 Michael Hanselmann
        raise errors.LockError(errmsg)
2837 aa74b828 Michael Hanselmann
2838 cc4c9b91 Michael Hanselmann
  @staticmethod
2839 cc4c9b91 Michael Hanselmann
  def _Lock(fd, flag, timeout):
2840 cc4c9b91 Michael Hanselmann
    try:
2841 cc4c9b91 Michael Hanselmann
      fcntl.flock(fd, flag)
2842 cc4c9b91 Michael Hanselmann
    except IOError, err:
2843 cc4c9b91 Michael Hanselmann
      if timeout is not None and err.errno == errno.EAGAIN:
2844 cc4c9b91 Michael Hanselmann
        raise RetryAgain()
2845 31892b4c Michael Hanselmann
2846 cc4c9b91 Michael Hanselmann
      logging.exception("fcntl.flock failed")
2847 cc4c9b91 Michael Hanselmann
      raise
2848 aa74b828 Michael Hanselmann
2849 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2850 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2851 a87b4824 Michael Hanselmann

2852 58885d79 Iustin Pop
    @type blocking: boolean
2853 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2854 58885d79 Iustin Pop
        can lock the file or return immediately
2855 58885d79 Iustin Pop
    @type timeout: int or None
2856 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2857 58885d79 Iustin Pop
        (in blocking mode)
2858 58885d79 Iustin Pop

2859 a87b4824 Michael Hanselmann
    """
2860 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2861 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2862 a87b4824 Michael Hanselmann
2863 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2864 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2865 a87b4824 Michael Hanselmann

2866 58885d79 Iustin Pop
    @type blocking: boolean
2867 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2868 58885d79 Iustin Pop
        can lock the file or return immediately
2869 58885d79 Iustin Pop
    @type timeout: int or None
2870 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2871 58885d79 Iustin Pop
        (in blocking mode)
2872 58885d79 Iustin Pop

2873 a87b4824 Michael Hanselmann
    """
2874 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2875 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2876 a87b4824 Michael Hanselmann
2877 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2878 a87b4824 Michael Hanselmann
    """Unlocks the file.
2879 a87b4824 Michael Hanselmann

2880 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2881 58885d79 Iustin Pop
    operation::
2882 58885d79 Iustin Pop

2883 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2884 58885d79 Iustin Pop
      operations.
2885 58885d79 Iustin Pop

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

2893 a87b4824 Michael Hanselmann
    """
2894 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2895 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2896 a87b4824 Michael Hanselmann
2897 a87b4824 Michael Hanselmann
2898 339be5a8 Michael Hanselmann
class LineSplitter:
2899 339be5a8 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
2900 339be5a8 Michael Hanselmann

2901 339be5a8 Michael Hanselmann
  Instances provide a file-like interface.
2902 339be5a8 Michael Hanselmann

2903 339be5a8 Michael Hanselmann
  """
2904 339be5a8 Michael Hanselmann
  def __init__(self, line_fn, *args):
2905 339be5a8 Michael Hanselmann
    """Initializes this class.
2906 339be5a8 Michael Hanselmann

2907 339be5a8 Michael Hanselmann
    @type line_fn: callable
2908 339be5a8 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
2909 339be5a8 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
2910 339be5a8 Michael Hanselmann

2911 339be5a8 Michael Hanselmann
    """
2912 339be5a8 Michael Hanselmann
    assert callable(line_fn)
2913 339be5a8 Michael Hanselmann
2914 339be5a8 Michael Hanselmann
    if args:
2915 339be5a8 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
2916 339be5a8 Michael Hanselmann
      self._line_fn = \
2917 339be5a8 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
2918 339be5a8 Michael Hanselmann
    else:
2919 339be5a8 Michael Hanselmann
      self._line_fn = line_fn
2920 339be5a8 Michael Hanselmann
2921 339be5a8 Michael Hanselmann
    self._lines = collections.deque()
2922 339be5a8 Michael Hanselmann
    self._buffer = ""
2923 339be5a8 Michael Hanselmann
2924 339be5a8 Michael Hanselmann
  def write(self, data):
2925 339be5a8 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
2926 339be5a8 Michael Hanselmann
    self._buffer = parts.pop()
2927 339be5a8 Michael Hanselmann
    self._lines.extend(parts)
2928 339be5a8 Michael Hanselmann
2929 339be5a8 Michael Hanselmann
  def flush(self):
2930 339be5a8 Michael Hanselmann
    while self._lines:
2931 339be5a8 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
2932 339be5a8 Michael Hanselmann
2933 339be5a8 Michael Hanselmann
  def close(self):
2934 339be5a8 Michael Hanselmann
    self.flush()
2935 339be5a8 Michael Hanselmann
    if self._buffer:
2936 339be5a8 Michael Hanselmann
      self._line_fn(self._buffer)
2937 339be5a8 Michael Hanselmann
2938 339be5a8 Michael Hanselmann
2939 451575de Guido Trotter
def SignalHandled(signums):
2940 451575de Guido Trotter
  """Signal Handled decoration.
2941 451575de Guido Trotter

2942 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2943 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2944 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2945 451575de Guido Trotter
  objects as values.
2946 451575de Guido Trotter

2947 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2948 451575de Guido Trotter
  with different handlers.
2949 451575de Guido Trotter

2950 451575de Guido Trotter
  @type signums: list
2951 451575de Guido Trotter
  @param signums: signals to intercept
2952 451575de Guido Trotter

2953 451575de Guido Trotter
  """
2954 451575de Guido Trotter
  def wrap(fn):
2955 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2956 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2957 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2958 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2959 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2960 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2961 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2962 451575de Guido Trotter
      else:
2963 451575de Guido Trotter
        signal_handlers = {}
2964 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2965 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2966 451575de Guido Trotter
      try:
2967 451575de Guido Trotter
        for sig in signums:
2968 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2969 451575de Guido Trotter
        return fn(*args, **kwargs)
2970 451575de Guido Trotter
      finally:
2971 451575de Guido Trotter
        sighandler.Reset()
2972 451575de Guido Trotter
    return sig_function
2973 451575de Guido Trotter
  return wrap
2974 451575de Guido Trotter
2975 451575de Guido Trotter
2976 de499029 Michael Hanselmann
class SignalHandler(object):
2977 de499029 Michael Hanselmann
  """Generic signal handler class.
2978 de499029 Michael Hanselmann

2979 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2980 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2981 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2982 58885d79 Iustin Pop
  signal was sent.
2983 58885d79 Iustin Pop

2984 58885d79 Iustin Pop
  @type signum: list
2985 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2986 58885d79 Iustin Pop
  @type called: boolean
2987 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2988 de499029 Michael Hanselmann

2989 de499029 Michael Hanselmann
  """
2990 de499029 Michael Hanselmann
  def __init__(self, signum):
2991 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2992 de499029 Michael Hanselmann

2993 58885d79 Iustin Pop
    @type signum: int or list of ints
2994 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2995 de499029 Michael Hanselmann

2996 de499029 Michael Hanselmann
    """
2997 6c52849e Guido Trotter
    self.signum = set(signum)
2998 de499029 Michael Hanselmann
    self.called = False
2999 de499029 Michael Hanselmann
3000 de499029 Michael Hanselmann
    self._previous = {}
3001 de499029 Michael Hanselmann
    try:
3002 de499029 Michael Hanselmann
      for signum in self.signum:
3003 de499029 Michael Hanselmann
        # Setup handler
3004 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
3005 de499029 Michael Hanselmann
        try:
3006 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
3007 de499029 Michael Hanselmann
        except:
3008 de499029 Michael Hanselmann
          # Restore previous handler
3009 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
3010 de499029 Michael Hanselmann
          raise
3011 de499029 Michael Hanselmann
    except:
3012 de499029 Michael Hanselmann
      # Reset all handlers
3013 de499029 Michael Hanselmann
      self.Reset()
3014 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
3015 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
3016 de499029 Michael Hanselmann
      raise
3017 de499029 Michael Hanselmann
3018 de499029 Michael Hanselmann
  def __del__(self):
3019 de499029 Michael Hanselmann
    self.Reset()
3020 de499029 Michael Hanselmann
3021 de499029 Michael Hanselmann
  def Reset(self):
3022 de499029 Michael Hanselmann
    """Restore previous handler.
3023 de499029 Michael Hanselmann

3024 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
3025 58885d79 Iustin Pop

3026 de499029 Michael Hanselmann
    """
3027 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
3028 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
3029 de499029 Michael Hanselmann
      # If successful, remove from dict
3030 de499029 Michael Hanselmann
      del self._previous[signum]
3031 de499029 Michael Hanselmann
3032 de499029 Michael Hanselmann
  def Clear(self):
3033 58885d79 Iustin Pop
    """Unsets the L{called} flag.
3034 de499029 Michael Hanselmann

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

3037 de499029 Michael Hanselmann
    """
3038 de499029 Michael Hanselmann
    self.called = False
3039 de499029 Michael Hanselmann
3040 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
3041 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
3042 de499029 Michael Hanselmann
    """Actual signal handling function.
3043 de499029 Michael Hanselmann

3044 de499029 Michael Hanselmann
    """
3045 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
3046 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
3047 de499029 Michael Hanselmann
    self.called = True
3048 a2d2e1a7 Iustin Pop
3049 a2d2e1a7 Iustin Pop
3050 a2d2e1a7 Iustin Pop
class FieldSet(object):
3051 a2d2e1a7 Iustin Pop
  """A simple field set.
3052 a2d2e1a7 Iustin Pop

3053 a2d2e1a7 Iustin Pop
  Among the features are:
3054 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
3055 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
3056 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
3057 a2d2e1a7 Iustin Pop

3058 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
3059 a2d2e1a7 Iustin Pop

3060 a2d2e1a7 Iustin Pop
  """
3061 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
3062 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
3063 a2d2e1a7 Iustin Pop
3064 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
3065 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
3066 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
3067 a2d2e1a7 Iustin Pop
3068 a2d2e1a7 Iustin Pop
  def Matches(self, field):
3069 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
3070 a2d2e1a7 Iustin Pop

3071 a2d2e1a7 Iustin Pop
    @type field: str
3072 a2d2e1a7 Iustin Pop
    @param field: the string to match
3073 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
3074 a2d2e1a7 Iustin Pop

3075 a2d2e1a7 Iustin Pop
    """
3076 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
3077 a2d2e1a7 Iustin Pop
      return m
3078 6c881c52 Iustin Pop
    return None
3079 a2d2e1a7 Iustin Pop
3080 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
3081 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
3082 a2d2e1a7 Iustin Pop

3083 a2d2e1a7 Iustin Pop
    @type items: list
3084 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
3085 a2d2e1a7 Iustin Pop
    @rtype: list
3086 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
3087 a2d2e1a7 Iustin Pop

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