Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 85414b69

History | View | Annotate | Download (53.3 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 sys
31 a8083063 Iustin Pop
import os
32 a8083063 Iustin Pop
import sha
33 a8083063 Iustin Pop
import time
34 113b55aa Iustin Pop
import subprocess
35 a8083063 Iustin Pop
import re
36 a8083063 Iustin Pop
import socket
37 a8083063 Iustin Pop
import tempfile
38 a8083063 Iustin Pop
import shutil
39 4ca1b175 Alexander Schreiber
import errno
40 2f8b60b3 Iustin Pop
import pwd
41 78feb6fb Guido Trotter
import itertools
42 9c233417 Iustin Pop
import select
43 9c233417 Iustin Pop
import fcntl
44 8f765069 Iustin Pop
import resource
45 bb698c1f Iustin Pop
import logging
46 de499029 Michael Hanselmann
import signal
47 9c233417 Iustin Pop
48 9c233417 Iustin Pop
from cStringIO import StringIO
49 a8083063 Iustin Pop
50 a8083063 Iustin Pop
from ganeti import errors
51 3aecd2c7 Iustin Pop
from ganeti import constants
52 a8083063 Iustin Pop
53 16abfbc2 Alexander Schreiber
54 a8083063 Iustin Pop
_locksheld = []
55 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
56 a8083063 Iustin Pop
57 f362096f Iustin Pop
debug = False
58 e67bd559 Michael Hanselmann
debug_locks = False
59 58885d79 Iustin Pop
60 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
61 b74159ee Iustin Pop
no_fork = False
62 f362096f Iustin Pop
63 7c0d6283 Michael Hanselmann
64 a8083063 Iustin Pop
class RunResult(object):
65 58885d79 Iustin Pop
  """Holds the result of running external programs.
66 58885d79 Iustin Pop

67 58885d79 Iustin Pop
  @type exit_code: int
68 58885d79 Iustin Pop
  @ivar exit_code: the exit code of the program, or None (if the program
69 58885d79 Iustin Pop
      didn't exit())
70 58885d79 Iustin Pop
  @type signal: int or None
71 58885d79 Iustin Pop
  @ivar signal: the signal that caused the program to finish, or None
72 58885d79 Iustin Pop
      (if the program wasn't terminated by a signal)
73 58885d79 Iustin Pop
  @type stdout: str
74 58885d79 Iustin Pop
  @ivar stdout: the standard output of the program
75 58885d79 Iustin Pop
  @type stderr: str
76 58885d79 Iustin Pop
  @ivar stderr: the standard error of the program
77 58885d79 Iustin Pop
  @type failed: boolean
78 58885d79 Iustin Pop
  @ivar failed: True in case the program was
79 58885d79 Iustin Pop
      terminated by a signal or exited with a non-zero exit code
80 58885d79 Iustin Pop
  @ivar fail_reason: a string detailing the termination reason
81 a8083063 Iustin Pop

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

109 a8083063 Iustin Pop
    """
110 a8083063 Iustin Pop
    return self.stdout + self.stderr
111 a8083063 Iustin Pop
112 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
113 a8083063 Iustin Pop
114 a8083063 Iustin Pop
115 8797df43 Iustin Pop
def RunCmd(cmd, env=None, output=None, cwd='/'):
116 a8083063 Iustin Pop
  """Execute a (shell) command.
117 a8083063 Iustin Pop

118 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
119 a8083063 Iustin Pop
  closed.
120 a8083063 Iustin Pop

121 e326d4e5 Guido Trotter
  @type  cmd: string or list
122 36117c2b Iustin Pop
  @param cmd: Command to run
123 2557ff82 Guido Trotter
  @type env: dict
124 58885d79 Iustin Pop
  @param env: Additional environment
125 36117c2b Iustin Pop
  @type output: str
126 58885d79 Iustin Pop
  @param output: if desired, the output of the command can be
127 36117c2b Iustin Pop
      saved in a file instead of the RunResult instance; this
128 36117c2b Iustin Pop
      parameter denotes the file name (if not None)
129 8797df43 Iustin Pop
  @type cwd: string
130 8797df43 Iustin Pop
  @param cwd: if specified, will be used as the working
131 8797df43 Iustin Pop
      directory for the command; the default will be /
132 36117c2b Iustin Pop
  @rtype: L{RunResult}
133 58885d79 Iustin Pop
  @return: RunResult instance
134 58885d79 Iustin Pop
  @raise erors.ProgrammerError: if we call this when forks are disabled
135 a8083063 Iustin Pop

136 a8083063 Iustin Pop
  """
137 b74159ee Iustin Pop
  if no_fork:
138 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
139 b74159ee Iustin Pop
140 a8083063 Iustin Pop
  if isinstance(cmd, list):
141 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
142 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
143 113b55aa Iustin Pop
    shell = False
144 113b55aa Iustin Pop
  else:
145 113b55aa Iustin Pop
    strcmd = cmd
146 113b55aa Iustin Pop
    shell = True
147 bb698c1f Iustin Pop
  logging.debug("RunCmd '%s'", strcmd)
148 2557ff82 Guido Trotter
149 2557ff82 Guido Trotter
  cmd_env = os.environ.copy()
150 2557ff82 Guido Trotter
  cmd_env["LC_ALL"] = "C"
151 2557ff82 Guido Trotter
  if env is not None:
152 2557ff82 Guido Trotter
    cmd_env.update(env)
153 2557ff82 Guido Trotter
154 36117c2b Iustin Pop
  if output is None:
155 8797df43 Iustin Pop
    out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
156 36117c2b Iustin Pop
  else:
157 8797df43 Iustin Pop
    status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
158 36117c2b Iustin Pop
    out = err = ""
159 36117c2b Iustin Pop
160 36117c2b Iustin Pop
  if status >= 0:
161 36117c2b Iustin Pop
    exitcode = status
162 36117c2b Iustin Pop
    signal_ = None
163 36117c2b Iustin Pop
  else:
164 36117c2b Iustin Pop
    exitcode = None
165 36117c2b Iustin Pop
    signal_ = -status
166 36117c2b Iustin Pop
167 36117c2b Iustin Pop
  return RunResult(exitcode, signal_, out, err, strcmd)
168 36117c2b Iustin Pop
169 ae59efea Michael Hanselmann
170 8797df43 Iustin Pop
def _RunCmdPipe(cmd, env, via_shell, cwd):
171 36117c2b Iustin Pop
  """Run a command and return its output.
172 36117c2b Iustin Pop

173 36117c2b Iustin Pop
  @type  cmd: string or list
174 36117c2b Iustin Pop
  @param cmd: Command to run
175 36117c2b Iustin Pop
  @type env: dict
176 36117c2b Iustin Pop
  @param env: The environment to use
177 36117c2b Iustin Pop
  @type via_shell: bool
178 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
179 8797df43 Iustin Pop
  @type cwd: string
180 8797df43 Iustin Pop
  @param cwd: the working directory for the program
181 36117c2b Iustin Pop
  @rtype: tuple
182 36117c2b Iustin Pop
  @return: (out, err, status)
183 36117c2b Iustin Pop

184 36117c2b Iustin Pop
  """
185 9c233417 Iustin Pop
  poller = select.poll()
186 36117c2b Iustin Pop
  child = subprocess.Popen(cmd, shell=via_shell,
187 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
188 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
189 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
190 8797df43 Iustin Pop
                           close_fds=True, env=env,
191 8797df43 Iustin Pop
                           cwd=cwd)
192 113b55aa Iustin Pop
193 113b55aa Iustin Pop
  child.stdin.close()
194 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
195 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
196 9c233417 Iustin Pop
  out = StringIO()
197 9c233417 Iustin Pop
  err = StringIO()
198 9c233417 Iustin Pop
  fdmap = {
199 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
200 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
201 9c233417 Iustin Pop
    }
202 9c233417 Iustin Pop
  for fd in fdmap:
203 9c233417 Iustin Pop
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
204 9c233417 Iustin Pop
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
205 9c233417 Iustin Pop
206 9c233417 Iustin Pop
  while fdmap:
207 bf988c29 Guido Trotter
    try:
208 bf988c29 Guido Trotter
      pollresult = poller.poll()
209 bf988c29 Guido Trotter
    except EnvironmentError, eerr:
210 bf988c29 Guido Trotter
      if eerr.errno == errno.EINTR:
211 bf988c29 Guido Trotter
        continue
212 bf988c29 Guido Trotter
      raise
213 bf988c29 Guido Trotter
    except select.error, serr:
214 bf988c29 Guido Trotter
      if serr[0] == errno.EINTR:
215 bf988c29 Guido Trotter
        continue
216 bf988c29 Guido Trotter
      raise
217 bf988c29 Guido Trotter
218 bf988c29 Guido Trotter
    for fd, event in pollresult:
219 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
220 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
221 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
222 9c233417 Iustin Pop
        if not data:
223 9c233417 Iustin Pop
          poller.unregister(fd)
224 9c233417 Iustin Pop
          del fdmap[fd]
225 9c233417 Iustin Pop
          continue
226 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
227 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
228 9c233417 Iustin Pop
          event & select.POLLERR):
229 9c233417 Iustin Pop
        poller.unregister(fd)
230 9c233417 Iustin Pop
        del fdmap[fd]
231 9c233417 Iustin Pop
232 9c233417 Iustin Pop
  out = out.getvalue()
233 9c233417 Iustin Pop
  err = err.getvalue()
234 a8083063 Iustin Pop
235 a8083063 Iustin Pop
  status = child.wait()
236 36117c2b Iustin Pop
  return out, err, status
237 a8083063 Iustin Pop
238 36117c2b Iustin Pop
239 8797df43 Iustin Pop
def _RunCmdFile(cmd, env, via_shell, output, cwd):
240 36117c2b Iustin Pop
  """Run a command and save its output to a file.
241 36117c2b Iustin Pop

242 36117c2b Iustin Pop
  @type  cmd: string or list
243 36117c2b Iustin Pop
  @param cmd: Command to run
244 36117c2b Iustin Pop
  @type env: dict
245 36117c2b Iustin Pop
  @param env: The environment to use
246 36117c2b Iustin Pop
  @type via_shell: bool
247 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
248 36117c2b Iustin Pop
  @type output: str
249 36117c2b Iustin Pop
  @param output: the filename in which to save the output
250 8797df43 Iustin Pop
  @type cwd: string
251 8797df43 Iustin Pop
  @param cwd: the working directory for the program
252 36117c2b Iustin Pop
  @rtype: int
253 36117c2b Iustin Pop
  @return: the exit status
254 36117c2b Iustin Pop

255 36117c2b Iustin Pop
  """
256 36117c2b Iustin Pop
  fh = open(output, "a")
257 36117c2b Iustin Pop
  try:
258 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
259 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
260 36117c2b Iustin Pop
                             stdout=fh,
261 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
262 8797df43 Iustin Pop
                             close_fds=True, env=env,
263 8797df43 Iustin Pop
                             cwd=cwd)
264 36117c2b Iustin Pop
265 36117c2b Iustin Pop
    child.stdin.close()
266 36117c2b Iustin Pop
    status = child.wait()
267 36117c2b Iustin Pop
  finally:
268 36117c2b Iustin Pop
    fh.close()
269 36117c2b Iustin Pop
  return status
270 a8083063 Iustin Pop
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
def RemoveFile(filename):
273 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
276 a8083063 Iustin Pop
  errors are passed.
277 a8083063 Iustin Pop

278 58885d79 Iustin Pop
  @type filename: str
279 58885d79 Iustin Pop
  @param filename: the file to be removed
280 58885d79 Iustin Pop

281 a8083063 Iustin Pop
  """
282 a8083063 Iustin Pop
  try:
283 a8083063 Iustin Pop
    os.unlink(filename)
284 a8083063 Iustin Pop
  except OSError, err:
285 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
286 a8083063 Iustin Pop
      raise
287 a8083063 Iustin Pop
288 a8083063 Iustin Pop
289 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
290 6e797216 Michael Hanselmann
  """Renames a file.
291 6e797216 Michael Hanselmann

292 6e797216 Michael Hanselmann
  @type old: string
293 6e797216 Michael Hanselmann
  @param old: Original path
294 6e797216 Michael Hanselmann
  @type new: string
295 6e797216 Michael Hanselmann
  @param new: New path
296 6e797216 Michael Hanselmann
  @type mkdir: bool
297 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
298 6e797216 Michael Hanselmann
  @type mkdir_mode: int
299 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
300 6e797216 Michael Hanselmann

301 6e797216 Michael Hanselmann
  """
302 6e797216 Michael Hanselmann
  try:
303 6e797216 Michael Hanselmann
    return os.rename(old, new)
304 6e797216 Michael Hanselmann
  except OSError, err:
305 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
306 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
307 6e797216 Michael Hanselmann
    # as efficient.
308 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
309 6e797216 Michael Hanselmann
      # Create directory and try again
310 6e797216 Michael Hanselmann
      os.makedirs(os.path.dirname(new), mkdir_mode)
311 6e797216 Michael Hanselmann
      return os.rename(old, new)
312 6e797216 Michael Hanselmann
    raise
313 6e797216 Michael Hanselmann
314 6e797216 Michael Hanselmann
315 a8083063 Iustin Pop
def _FingerprintFile(filename):
316 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
317 a8083063 Iustin Pop

318 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
319 a8083063 Iustin Pop
  instead.
320 a8083063 Iustin Pop

321 58885d79 Iustin Pop
  @type filename: str
322 58885d79 Iustin Pop
  @param filename: the filename to checksum
323 58885d79 Iustin Pop
  @rtype: str
324 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
325 58885d79 Iustin Pop
      of the file
326 a8083063 Iustin Pop

327 a8083063 Iustin Pop
  """
328 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
329 a8083063 Iustin Pop
    return None
330 a8083063 Iustin Pop
331 a8083063 Iustin Pop
  f = open(filename)
332 a8083063 Iustin Pop
333 a8083063 Iustin Pop
  fp = sha.sha()
334 a8083063 Iustin Pop
  while True:
335 a8083063 Iustin Pop
    data = f.read(4096)
336 a8083063 Iustin Pop
    if not data:
337 a8083063 Iustin Pop
      break
338 a8083063 Iustin Pop
339 a8083063 Iustin Pop
    fp.update(data)
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
  return fp.hexdigest()
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
def FingerprintFiles(files):
345 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
346 a8083063 Iustin Pop

347 58885d79 Iustin Pop
  @type files: list
348 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
349 58885d79 Iustin Pop
  @rtype: dict
350 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
351 58885d79 Iustin Pop
      existing files
352 a8083063 Iustin Pop

353 a8083063 Iustin Pop
  """
354 a8083063 Iustin Pop
  ret = {}
355 a8083063 Iustin Pop
356 a8083063 Iustin Pop
  for filename in files:
357 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
358 a8083063 Iustin Pop
    if cksum:
359 a8083063 Iustin Pop
      ret[filename] = cksum
360 a8083063 Iustin Pop
361 a8083063 Iustin Pop
  return ret
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
365 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
366 a8083063 Iustin Pop

367 58885d79 Iustin Pop
  For the given dictionaries I{target} and I{template}, ensure
368 58885d79 Iustin Pop
  I{target} has all the keys from I{template}. Missing keys are added
369 58885d79 Iustin Pop
  with values from template.
370 a8083063 Iustin Pop

371 58885d79 Iustin Pop
  @type target: dict
372 58885d79 Iustin Pop
  @param target: the dictionary to update
373 58885d79 Iustin Pop
  @type template: dict
374 58885d79 Iustin Pop
  @param template: the dictionary holding the default values
375 58885d79 Iustin Pop
  @type logname: str or None
376 58885d79 Iustin Pop
  @param logname: if not None, causes the missing keys to be
377 58885d79 Iustin Pop
      logged with this name
378 a8083063 Iustin Pop

379 a8083063 Iustin Pop
  """
380 a8083063 Iustin Pop
  missing = []
381 a8083063 Iustin Pop
  for k in template:
382 a8083063 Iustin Pop
    if k not in target:
383 a8083063 Iustin Pop
      missing.append(k)
384 a8083063 Iustin Pop
      target[k] = template[k]
385 a8083063 Iustin Pop
386 a8083063 Iustin Pop
  if missing and logname:
387 bb698c1f Iustin Pop
    logging.warning('%s missing keys %s', logname, ', '.join(missing))
388 a8083063 Iustin Pop
389 a8083063 Iustin Pop
390 a8083063 Iustin Pop
def IsProcessAlive(pid):
391 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
392 a8083063 Iustin Pop

393 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
394 44bf25ff Iustin Pop
      will be returned as alive
395 58885d79 Iustin Pop
  @type pid: int
396 58885d79 Iustin Pop
  @param pid: the process ID to check
397 58885d79 Iustin Pop
  @rtype: boolean
398 58885d79 Iustin Pop
  @return: True if the process exists
399 a8083063 Iustin Pop

400 a8083063 Iustin Pop
  """
401 d9f311d7 Iustin Pop
  if pid <= 0:
402 d9f311d7 Iustin Pop
    return False
403 d9f311d7 Iustin Pop
404 a8083063 Iustin Pop
  try:
405 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
406 44bf25ff Iustin Pop
    return True
407 44bf25ff Iustin Pop
  except EnvironmentError, err:
408 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
409 a8083063 Iustin Pop
      return False
410 44bf25ff Iustin Pop
    raise
411 a8083063 Iustin Pop
412 a8083063 Iustin Pop
413 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
414 58885d79 Iustin Pop
  """Read a pid from a file.
415 fee80e90 Guido Trotter

416 58885d79 Iustin Pop
  @type  pidfile: string
417 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
418 58885d79 Iustin Pop
  @rtype: int
419 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
420 d9f311d7 Iustin Pop
           otherwise 0
421 fee80e90 Guido Trotter

422 fee80e90 Guido Trotter
  """
423 fee80e90 Guido Trotter
  try:
424 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
425 d9f311d7 Iustin Pop
  except EnvironmentError, err:
426 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
427 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
428 d9f311d7 Iustin Pop
    return 0
429 fee80e90 Guido Trotter
430 fee80e90 Guido Trotter
  try:
431 fee80e90 Guido Trotter
    pid = int(pf.read())
432 d9f311d7 Iustin Pop
  except ValueError, err:
433 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
434 d9f311d7 Iustin Pop
    return 0
435 fee80e90 Guido Trotter
436 d9f311d7 Iustin Pop
  return pid
437 fee80e90 Guido Trotter
438 fee80e90 Guido Trotter
439 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
440 a8083063 Iustin Pop
  """Try to match a name against a list.
441 a8083063 Iustin Pop

442 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
443 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
444 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
445 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
446 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
447 58885d79 Iustin Pop
  'test1.example.org']}).
448 a8083063 Iustin Pop

449 58885d79 Iustin Pop
  @type key: str
450 58885d79 Iustin Pop
  @param key: the name to be searched
451 58885d79 Iustin Pop
  @type name_list: list
452 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
453 a8083063 Iustin Pop

454 58885d79 Iustin Pop
  @rtype: None or str
455 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
456 58885d79 Iustin Pop
      otherwise the element from the list which matches
457 a8083063 Iustin Pop

458 a8083063 Iustin Pop
  """
459 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
460 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
461 a8083063 Iustin Pop
  if len(names_filtered) != 1:
462 a8083063 Iustin Pop
    return None
463 a8083063 Iustin Pop
  return names_filtered[0]
464 a8083063 Iustin Pop
465 a8083063 Iustin Pop
466 bcf043c9 Iustin Pop
class HostInfo:
467 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
468 bcf043c9 Iustin Pop

469 bcf043c9 Iustin Pop
  """
470 89e1fc26 Iustin Pop
  def __init__(self, name=None):
471 bcf043c9 Iustin Pop
    """Initialize the host name object.
472 bcf043c9 Iustin Pop

473 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
474 89e1fc26 Iustin Pop
    name.
475 bcf043c9 Iustin Pop

476 bcf043c9 Iustin Pop
    """
477 89e1fc26 Iustin Pop
    if name is None:
478 89e1fc26 Iustin Pop
      name = self.SysName()
479 89e1fc26 Iustin Pop
480 89e1fc26 Iustin Pop
    self.query = name
481 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
482 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
483 bcf043c9 Iustin Pop
484 c8a0948f Michael Hanselmann
  def ShortName(self):
485 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
486 c8a0948f Michael Hanselmann

487 c8a0948f Michael Hanselmann
    """
488 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
489 c8a0948f Michael Hanselmann
490 89e1fc26 Iustin Pop
  @staticmethod
491 89e1fc26 Iustin Pop
  def SysName():
492 89e1fc26 Iustin Pop
    """Return the current system's name.
493 bcf043c9 Iustin Pop

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

496 89e1fc26 Iustin Pop
    """
497 89e1fc26 Iustin Pop
    return socket.gethostname()
498 a8083063 Iustin Pop
499 89e1fc26 Iustin Pop
  @staticmethod
500 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
501 89e1fc26 Iustin Pop
    """Look up hostname
502 a8083063 Iustin Pop

503 58885d79 Iustin Pop
    @type hostname: str
504 58885d79 Iustin Pop
    @param hostname: hostname to look up
505 89e1fc26 Iustin Pop

506 58885d79 Iustin Pop
    @rtype: tuple
507 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
508 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
509 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
510 89e1fc26 Iustin Pop

511 89e1fc26 Iustin Pop
    """
512 89e1fc26 Iustin Pop
    try:
513 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
514 89e1fc26 Iustin Pop
    except socket.gaierror, err:
515 89e1fc26 Iustin Pop
      # hostname not found in DNS
516 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
517 a8083063 Iustin Pop
518 89e1fc26 Iustin Pop
    return result
519 a8083063 Iustin Pop
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
def ListVolumeGroups():
522 a8083063 Iustin Pop
  """List volume groups and their size
523 a8083063 Iustin Pop

524 58885d79 Iustin Pop
  @rtype: dict
525 58885d79 Iustin Pop
  @return:
526 58885d79 Iustin Pop
       Dictionary with keys volume name and values
527 58885d79 Iustin Pop
       the size of the volume
528 a8083063 Iustin Pop

529 a8083063 Iustin Pop
  """
530 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
531 a8083063 Iustin Pop
  result = RunCmd(command)
532 a8083063 Iustin Pop
  retval = {}
533 a8083063 Iustin Pop
  if result.failed:
534 a8083063 Iustin Pop
    return retval
535 a8083063 Iustin Pop
536 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
537 a8083063 Iustin Pop
    try:
538 a8083063 Iustin Pop
      name, size = line.split()
539 a8083063 Iustin Pop
      size = int(float(size))
540 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
541 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
542 a8083063 Iustin Pop
      continue
543 a8083063 Iustin Pop
544 a8083063 Iustin Pop
    retval[name] = size
545 a8083063 Iustin Pop
546 a8083063 Iustin Pop
  return retval
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
549 a8083063 Iustin Pop
def BridgeExists(bridge):
550 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
551 a8083063 Iustin Pop

552 58885d79 Iustin Pop
  @type bridge: str
553 58885d79 Iustin Pop
  @param bridge: the bridge name to check
554 58885d79 Iustin Pop
  @rtype: boolean
555 58885d79 Iustin Pop
  @return: True if it does
556 a8083063 Iustin Pop

557 a8083063 Iustin Pop
  """
558 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
561 42ce2e13 Guido Trotter
def CheckBEParams(beparams):
562 42ce2e13 Guido Trotter
  """Checks whether the user-supplied be-params are valid,
563 42ce2e13 Guido Trotter
  and converts them from string format where appropriate.
564 42ce2e13 Guido Trotter

565 42ce2e13 Guido Trotter
  @type beparams: dict
566 42ce2e13 Guido Trotter
  @param beparams: new params dict
567 42ce2e13 Guido Trotter

568 42ce2e13 Guido Trotter
  """
569 42ce2e13 Guido Trotter
  if beparams:
570 42ce2e13 Guido Trotter
    for item in beparams:
571 42ce2e13 Guido Trotter
      if item not in constants.BES_PARAMETERS:
572 42ce2e13 Guido Trotter
        raise errors.OpPrereqError("Unknown backend parameter %s" % item)
573 42ce2e13 Guido Trotter
      if item in (constants.BE_MEMORY, constants.BE_VCPUS):
574 42ce2e13 Guido Trotter
        val = beparams[item]
575 42ce2e13 Guido Trotter
        if val != constants.VALUE_DEFAULT:
576 42ce2e13 Guido Trotter
          try:
577 42ce2e13 Guido Trotter
            val = int(val)
578 42ce2e13 Guido Trotter
          except ValueError, err:
579 26f15862 Iustin Pop
            raise errors.OpPrereqError("Invalid %s size: %s" % (item, err))
580 42ce2e13 Guido Trotter
          beparams[item] = val
581 42ce2e13 Guido Trotter
      if item in (constants.BE_AUTO_BALANCE):
582 42ce2e13 Guido Trotter
        val = beparams[item]
583 90eb468c Guido Trotter
        if not isinstance(val, bool):
584 90eb468c Guido Trotter
          if val == constants.VALUE_TRUE:
585 90eb468c Guido Trotter
            beparams[item] = True
586 90eb468c Guido Trotter
          elif val == constants.VALUE_FALSE:
587 90eb468c Guido Trotter
            beparams[item] = False
588 90eb468c Guido Trotter
          else:
589 90eb468c Guido Trotter
            raise errors.OpPrereqError("Invalid %s value: %s" % (item, val))
590 42ce2e13 Guido Trotter
591 42ce2e13 Guido Trotter
592 a8083063 Iustin Pop
def NiceSort(name_list):
593 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
594 a8083063 Iustin Pop

595 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
596 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
597 58885d79 Iustin Pop
  'a11']}.
598 a8083063 Iustin Pop

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

603 58885d79 Iustin Pop
  @type name_list: list
604 58885d79 Iustin Pop
  @param name_list: the names to be sorted
605 58885d79 Iustin Pop
  @rtype: list
606 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
607 a8083063 Iustin Pop

608 a8083063 Iustin Pop
  """
609 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
610 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
611 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
612 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
613 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
614 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
615 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
616 a8083063 Iustin Pop
  def _TryInt(val):
617 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
618 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
619 a8083063 Iustin Pop
      return val
620 a8083063 Iustin Pop
    rval = int(val)
621 a8083063 Iustin Pop
    return rval
622 a8083063 Iustin Pop
623 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
624 a8083063 Iustin Pop
             for name in name_list]
625 a8083063 Iustin Pop
  to_sort.sort()
626 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
627 a8083063 Iustin Pop
628 a8083063 Iustin Pop
629 a8083063 Iustin Pop
def TryConvert(fn, val):
630 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
631 a8083063 Iustin Pop

632 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
633 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
634 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
635 58885d79 Iustin Pop
  exceptions are propagated to the caller.
636 58885d79 Iustin Pop

637 58885d79 Iustin Pop
  @type fn: callable
638 58885d79 Iustin Pop
  @param fn: function to apply to the value
639 58885d79 Iustin Pop
  @param val: the value to be converted
640 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
641 58885d79 Iustin Pop
      otherwise the original value.
642 a8083063 Iustin Pop

643 a8083063 Iustin Pop
  """
644 a8083063 Iustin Pop
  try:
645 a8083063 Iustin Pop
    nv = fn(val)
646 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
647 a8083063 Iustin Pop
    nv = val
648 a8083063 Iustin Pop
  return nv
649 a8083063 Iustin Pop
650 a8083063 Iustin Pop
651 a8083063 Iustin Pop
def IsValidIP(ip):
652 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
653 a8083063 Iustin Pop

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

657 58885d79 Iustin Pop
  @type ip: str
658 58885d79 Iustin Pop
  @param ip: the address to be checked
659 58885d79 Iustin Pop
  @rtype: a regular expression match object
660 58885d79 Iustin Pop
  @return: a regular epression match object, or None if the
661 58885d79 Iustin Pop
      address is not valid
662 a8083063 Iustin Pop

663 a8083063 Iustin Pop
  """
664 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
665 58885d79 Iustin Pop
  #TODO: convert and return only boolean
666 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
667 a8083063 Iustin Pop
668 a8083063 Iustin Pop
669 a8083063 Iustin Pop
def IsValidShellParam(word):
670 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
671 a8083063 Iustin Pop

672 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
673 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
674 a8083063 Iustin Pop
  the actual command.
675 a8083063 Iustin Pop

676 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
677 a8083063 Iustin Pop
  side.
678 a8083063 Iustin Pop

679 58885d79 Iustin Pop
  @type word: str
680 58885d79 Iustin Pop
  @param word: the word to check
681 58885d79 Iustin Pop
  @rtype: boolean
682 58885d79 Iustin Pop
  @return: True if the word is 'safe'
683 58885d79 Iustin Pop

684 a8083063 Iustin Pop
  """
685 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
686 a8083063 Iustin Pop
687 a8083063 Iustin Pop
688 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
689 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
690 a8083063 Iustin Pop

691 a8083063 Iustin Pop
  This function will check all arguments in the args list so that they
692 a8083063 Iustin Pop
  are valid shell parameters (i.e. they don't contain shell
693 a8083063 Iustin Pop
  metacharaters). If everything is ok, it will return the result of
694 a8083063 Iustin Pop
  template % args.
695 a8083063 Iustin Pop

696 58885d79 Iustin Pop
  @type template: str
697 58885d79 Iustin Pop
  @param template: the string holding the template for the
698 58885d79 Iustin Pop
      string formatting
699 58885d79 Iustin Pop
  @rtype: str
700 58885d79 Iustin Pop
  @return: the expanded command line
701 58885d79 Iustin Pop

702 a8083063 Iustin Pop
  """
703 a8083063 Iustin Pop
  for word in args:
704 a8083063 Iustin Pop
    if not IsValidShellParam(word):
705 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
706 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
707 a8083063 Iustin Pop
  return template % args
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
710 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
711 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
712 a8083063 Iustin Pop

713 58885d79 Iustin Pop
  @type value: int
714 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
715 9fbfbb7b Iustin Pop
  @type units: char
716 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
717 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
718 9fbfbb7b Iustin Pop
      - 'm' for MiBs
719 9fbfbb7b Iustin Pop
      - 'g' for GiBs
720 9fbfbb7b Iustin Pop
      - 't' for TiBs
721 58885d79 Iustin Pop
  @rtype: str
722 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
723 a8083063 Iustin Pop

724 a8083063 Iustin Pop
  """
725 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
726 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
727 a8083063 Iustin Pop
728 9fbfbb7b Iustin Pop
  suffix = ''
729 9fbfbb7b Iustin Pop
730 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
731 9fbfbb7b Iustin Pop
    if units == 'h':
732 9fbfbb7b Iustin Pop
      suffix = 'M'
733 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
734 9fbfbb7b Iustin Pop
735 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
736 9fbfbb7b Iustin Pop
    if units == 'h':
737 9fbfbb7b Iustin Pop
      suffix = 'G'
738 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
739 a8083063 Iustin Pop
740 a8083063 Iustin Pop
  else:
741 9fbfbb7b Iustin Pop
    if units == 'h':
742 9fbfbb7b Iustin Pop
      suffix = 'T'
743 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
744 a8083063 Iustin Pop
745 a8083063 Iustin Pop
746 a8083063 Iustin Pop
def ParseUnit(input_string):
747 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
748 a8083063 Iustin Pop

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

753 a8083063 Iustin Pop
  """
754 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
755 a8083063 Iustin Pop
  if not m:
756 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
757 a8083063 Iustin Pop
758 a8083063 Iustin Pop
  value = float(m.groups()[0])
759 a8083063 Iustin Pop
760 a8083063 Iustin Pop
  unit = m.groups()[1]
761 a8083063 Iustin Pop
  if unit:
762 a8083063 Iustin Pop
    lcunit = unit.lower()
763 a8083063 Iustin Pop
  else:
764 a8083063 Iustin Pop
    lcunit = 'm'
765 a8083063 Iustin Pop
766 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
767 a8083063 Iustin Pop
    # Value already in MiB
768 a8083063 Iustin Pop
    pass
769 a8083063 Iustin Pop
770 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
771 a8083063 Iustin Pop
    value *= 1024
772 a8083063 Iustin Pop
773 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
774 a8083063 Iustin Pop
    value *= 1024 * 1024
775 a8083063 Iustin Pop
776 a8083063 Iustin Pop
  else:
777 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
778 a8083063 Iustin Pop
779 a8083063 Iustin Pop
  # Make sure we round up
780 a8083063 Iustin Pop
  if int(value) < value:
781 a8083063 Iustin Pop
    value += 1
782 a8083063 Iustin Pop
783 a8083063 Iustin Pop
  # Round up to the next multiple of 4
784 a8083063 Iustin Pop
  value = int(value)
785 a8083063 Iustin Pop
  if value % 4:
786 a8083063 Iustin Pop
    value += 4 - value % 4
787 a8083063 Iustin Pop
788 a8083063 Iustin Pop
  return value
789 a8083063 Iustin Pop
790 a8083063 Iustin Pop
791 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
792 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
793 a8083063 Iustin Pop

794 58885d79 Iustin Pop
  @type file_name: str
795 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
796 58885d79 Iustin Pop
  @type key: str
797 58885d79 Iustin Pop
  @param key: string containing key
798 58885d79 Iustin Pop

799 a8083063 Iustin Pop
  """
800 a8083063 Iustin Pop
  key_fields = key.split()
801 a8083063 Iustin Pop
802 a8083063 Iustin Pop
  f = open(file_name, 'a+')
803 a8083063 Iustin Pop
  try:
804 a8083063 Iustin Pop
    nl = True
805 a8083063 Iustin Pop
    for line in f:
806 a8083063 Iustin Pop
      # Ignore whitespace changes
807 a8083063 Iustin Pop
      if line.split() == key_fields:
808 a8083063 Iustin Pop
        break
809 a8083063 Iustin Pop
      nl = line.endswith('\n')
810 a8083063 Iustin Pop
    else:
811 a8083063 Iustin Pop
      if not nl:
812 a8083063 Iustin Pop
        f.write("\n")
813 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
814 a8083063 Iustin Pop
      f.write("\n")
815 a8083063 Iustin Pop
      f.flush()
816 a8083063 Iustin Pop
  finally:
817 a8083063 Iustin Pop
    f.close()
818 a8083063 Iustin Pop
819 a8083063 Iustin Pop
820 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
821 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
822 a8083063 Iustin Pop

823 58885d79 Iustin Pop
  @type file_name: str
824 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
825 58885d79 Iustin Pop
  @type key: str
826 58885d79 Iustin Pop
  @param key: string containing key
827 58885d79 Iustin Pop

828 a8083063 Iustin Pop
  """
829 a8083063 Iustin Pop
  key_fields = key.split()
830 a8083063 Iustin Pop
831 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
832 a8083063 Iustin Pop
  try:
833 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
834 a8083063 Iustin Pop
    try:
835 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
836 59f82e3f Michael Hanselmann
      try:
837 59f82e3f Michael Hanselmann
        for line in f:
838 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
839 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
840 59f82e3f Michael Hanselmann
            out.write(line)
841 899d2a81 Michael Hanselmann
842 899d2a81 Michael Hanselmann
        out.flush()
843 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
844 899d2a81 Michael Hanselmann
      finally:
845 899d2a81 Michael Hanselmann
        f.close()
846 899d2a81 Michael Hanselmann
    finally:
847 899d2a81 Michael Hanselmann
      out.close()
848 899d2a81 Michael Hanselmann
  except:
849 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
850 899d2a81 Michael Hanselmann
    raise
851 899d2a81 Michael Hanselmann
852 899d2a81 Michael Hanselmann
853 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
854 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
855 899d2a81 Michael Hanselmann

856 58885d79 Iustin Pop
  @type file_name: str
857 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
858 58885d79 Iustin Pop
  @type ip: str
859 58885d79 Iustin Pop
  @param ip: the IP address
860 58885d79 Iustin Pop
  @type hostname: str
861 58885d79 Iustin Pop
  @param hostname: the hostname to be added
862 58885d79 Iustin Pop
  @type aliases: list
863 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
864 58885d79 Iustin Pop

865 899d2a81 Michael Hanselmann
  """
866 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
867 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
868 7fbb1f65 Michael Hanselmann
869 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
870 899d2a81 Michael Hanselmann
  try:
871 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
872 9440aeab Michael Hanselmann
    try:
873 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
874 9440aeab Michael Hanselmann
      try:
875 9440aeab Michael Hanselmann
        for line in f:
876 9440aeab Michael Hanselmann
          fields = line.split()
877 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
878 9440aeab Michael Hanselmann
            continue
879 9440aeab Michael Hanselmann
          out.write(line)
880 9440aeab Michael Hanselmann
881 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
882 9440aeab Michael Hanselmann
        if aliases:
883 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
884 9440aeab Michael Hanselmann
        out.write('\n')
885 9440aeab Michael Hanselmann
886 9440aeab Michael Hanselmann
        out.flush()
887 2e3e75b7 Michael Hanselmann
        os.fsync(out)
888 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
889 9440aeab Michael Hanselmann
      finally:
890 9440aeab Michael Hanselmann
        f.close()
891 9440aeab Michael Hanselmann
    finally:
892 9440aeab Michael Hanselmann
      out.close()
893 9440aeab Michael Hanselmann
  except:
894 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
895 9440aeab Michael Hanselmann
    raise
896 899d2a81 Michael Hanselmann
897 899d2a81 Michael Hanselmann
898 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
899 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
900 d9c02ca6 Michael Hanselmann

901 58885d79 Iustin Pop
  @type hostname: str
902 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
903 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
904 58885d79 Iustin Pop

905 d9c02ca6 Michael Hanselmann
  """
906 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
907 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
908 d9c02ca6 Michael Hanselmann
909 d9c02ca6 Michael Hanselmann
910 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
911 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
912 899d2a81 Michael Hanselmann

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

915 58885d79 Iustin Pop
  @type file_name: str
916 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
917 58885d79 Iustin Pop
  @type hostname: str
918 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
919 58885d79 Iustin Pop

920 899d2a81 Michael Hanselmann
  """
921 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
922 899d2a81 Michael Hanselmann
  try:
923 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
924 899d2a81 Michael Hanselmann
    try:
925 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
926 899d2a81 Michael Hanselmann
      try:
927 899d2a81 Michael Hanselmann
        for line in f:
928 899d2a81 Michael Hanselmann
          fields = line.split()
929 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
930 899d2a81 Michael Hanselmann
            names = fields[1:]
931 899d2a81 Michael Hanselmann
            if hostname in names:
932 899d2a81 Michael Hanselmann
              while hostname in names:
933 899d2a81 Michael Hanselmann
                names.remove(hostname)
934 899d2a81 Michael Hanselmann
              if names:
935 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
936 899d2a81 Michael Hanselmann
              continue
937 899d2a81 Michael Hanselmann
938 899d2a81 Michael Hanselmann
          out.write(line)
939 59f82e3f Michael Hanselmann
940 59f82e3f Michael Hanselmann
        out.flush()
941 2e3e75b7 Michael Hanselmann
        os.fsync(out)
942 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
943 59f82e3f Michael Hanselmann
      finally:
944 59f82e3f Michael Hanselmann
        f.close()
945 a8083063 Iustin Pop
    finally:
946 59f82e3f Michael Hanselmann
      out.close()
947 59f82e3f Michael Hanselmann
  except:
948 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
949 59f82e3f Michael Hanselmann
    raise
950 a8083063 Iustin Pop
951 a8083063 Iustin Pop
952 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
953 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
954 d9c02ca6 Michael Hanselmann

955 58885d79 Iustin Pop
  @type hostname: str
956 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
957 58885d79 Iustin Pop
      full and shot name will be removed from
958 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
959 58885d79 Iustin Pop

960 d9c02ca6 Michael Hanselmann
  """
961 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
962 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
963 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
964 d9c02ca6 Michael Hanselmann
965 d9c02ca6 Michael Hanselmann
966 a8083063 Iustin Pop
def CreateBackup(file_name):
967 a8083063 Iustin Pop
  """Creates a backup of a file.
968 a8083063 Iustin Pop

969 58885d79 Iustin Pop
  @type file_name: str
970 58885d79 Iustin Pop
  @param file_name: file to be backed up
971 58885d79 Iustin Pop
  @rtype: str
972 58885d79 Iustin Pop
  @return: the path to the newly created backup
973 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
974 a8083063 Iustin Pop

975 a8083063 Iustin Pop
  """
976 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
977 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
978 3ecf6786 Iustin Pop
                                file_name)
979 a8083063 Iustin Pop
980 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
981 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
982 081b1e69 Michael Hanselmann
983 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
984 081b1e69 Michael Hanselmann
  try:
985 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
986 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
987 081b1e69 Michael Hanselmann
    try:
988 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
989 081b1e69 Michael Hanselmann
    finally:
990 081b1e69 Michael Hanselmann
      fdst.close()
991 081b1e69 Michael Hanselmann
  finally:
992 081b1e69 Michael Hanselmann
    fsrc.close()
993 081b1e69 Michael Hanselmann
994 a8083063 Iustin Pop
  return backup_name
995 a8083063 Iustin Pop
996 a8083063 Iustin Pop
997 a8083063 Iustin Pop
def ShellQuote(value):
998 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
999 3ecf6786 Iustin Pop

1000 58885d79 Iustin Pop
  @type value: str
1001 58885d79 Iustin Pop
  @param value: the argument to be quoted
1002 58885d79 Iustin Pop
  @rtype: str
1003 58885d79 Iustin Pop
  @return: the quoted value
1004 58885d79 Iustin Pop

1005 a8083063 Iustin Pop
  """
1006 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1007 a8083063 Iustin Pop
    return value
1008 a8083063 Iustin Pop
  else:
1009 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1010 a8083063 Iustin Pop
1011 a8083063 Iustin Pop
1012 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1013 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1014 58885d79 Iustin Pop

1015 58885d79 Iustin Pop
  @type args: list
1016 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1017 58885d79 Iustin Pop
  @rtype: str
1018 58885d79 Iustin Pop
  @return: the quoted arguments concatenaned with spaces
1019 a8083063 Iustin Pop

1020 a8083063 Iustin Pop
  """
1021 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1022 88d14415 Michael Hanselmann
1023 88d14415 Michael Hanselmann
1024 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1025 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1026 2c30e9d7 Alexander Schreiber

1027 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1028 58885d79 Iustin Pop
  to it.
1029 58885d79 Iustin Pop

1030 58885d79 Iustin Pop
  @type target: str
1031 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1032 58885d79 Iustin Pop
  @type port: int
1033 58885d79 Iustin Pop
  @param port: the port to connect to
1034 58885d79 Iustin Pop
  @type timeout: int
1035 58885d79 Iustin Pop
  @param timeout: the timeout on the connection attemp
1036 58885d79 Iustin Pop
  @type live_port_needed: boolean
1037 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1038 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1039 58885d79 Iustin Pop
  @type source: str or None
1040 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1041 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1042 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1043 2c30e9d7 Alexander Schreiber

1044 2c30e9d7 Alexander Schreiber
  """
1045 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1046 2c30e9d7 Alexander Schreiber
1047 0b5ad33e Iustin Pop
  success = False
1048 2c30e9d7 Alexander Schreiber
1049 b15d625f Iustin Pop
  if source is not None:
1050 b15d625f Iustin Pop
    try:
1051 b15d625f Iustin Pop
      sock.bind((source, 0))
1052 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
1053 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1054 b15d625f Iustin Pop
        success = False
1055 2c30e9d7 Alexander Schreiber
1056 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1057 2c30e9d7 Alexander Schreiber
1058 2c30e9d7 Alexander Schreiber
  try:
1059 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1060 2c30e9d7 Alexander Schreiber
    sock.close()
1061 2c30e9d7 Alexander Schreiber
    success = True
1062 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1063 2c30e9d7 Alexander Schreiber
    success = False
1064 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
1065 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1066 2c30e9d7 Alexander Schreiber
1067 2c30e9d7 Alexander Schreiber
  return success
1068 eedbda4b Michael Hanselmann
1069 eedbda4b Michael Hanselmann
1070 caad16e2 Iustin Pop
def OwnIpAddress(address):
1071 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1072 caad16e2 Iustin Pop

1073 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1074 caad16e2 Iustin Pop
  address.
1075 caad16e2 Iustin Pop

1076 caad16e2 Iustin Pop
  @type address: string
1077 caad16e2 Iustin Pop
  @param address: the addres to check
1078 caad16e2 Iustin Pop
  @rtype: bool
1079 58885d79 Iustin Pop
  @return: True if we own the address
1080 caad16e2 Iustin Pop

1081 caad16e2 Iustin Pop
  """
1082 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1083 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1084 caad16e2 Iustin Pop
1085 caad16e2 Iustin Pop
1086 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1087 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1088 58885d79 Iustin Pop

1089 58885d79 Iustin Pop
  @type path: str
1090 58885d79 Iustin Pop
  @param path: the directory to enumerate
1091 58885d79 Iustin Pop
  @rtype: list
1092 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1093 eedbda4b Michael Hanselmann

1094 eedbda4b Michael Hanselmann
  """
1095 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1096 f3299a07 Michael Hanselmann
  files.sort()
1097 f3299a07 Michael Hanselmann
  return files
1098 2f8b60b3 Iustin Pop
1099 2f8b60b3 Iustin Pop
1100 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1101 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1102 257f4c0a Iustin Pop

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

1107 2f8b60b3 Iustin Pop
  """
1108 2f8b60b3 Iustin Pop
  try:
1109 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1110 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1111 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1112 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1113 257f4c0a Iustin Pop
    else:
1114 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1115 257f4c0a Iustin Pop
                                   type(user))
1116 2f8b60b3 Iustin Pop
  except KeyError:
1117 2f8b60b3 Iustin Pop
    return default
1118 2f8b60b3 Iustin Pop
  return result.pw_dir
1119 59072e7e Michael Hanselmann
1120 59072e7e Michael Hanselmann
1121 24818e8f Michael Hanselmann
def NewUUID():
1122 59072e7e Michael Hanselmann
  """Returns a random UUID.
1123 59072e7e Michael Hanselmann

1124 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1125 58885d79 Iustin Pop
      filesystem.
1126 58885d79 Iustin Pop
  @rtype: str
1127 58885d79 Iustin Pop

1128 59072e7e Michael Hanselmann
  """
1129 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
1130 59072e7e Michael Hanselmann
  try:
1131 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
1132 59072e7e Michael Hanselmann
  finally:
1133 59072e7e Michael Hanselmann
    f.close()
1134 087b34fe Iustin Pop
1135 087b34fe Iustin Pop
1136 33081d90 Iustin Pop
def GenerateSecret():
1137 33081d90 Iustin Pop
  """Generates a random secret.
1138 33081d90 Iustin Pop

1139 33081d90 Iustin Pop
  This will generate a pseudo-random secret, and return its sha digest
1140 33081d90 Iustin Pop
  (so that it can be used where an ASCII string is needed).
1141 33081d90 Iustin Pop

1142 58885d79 Iustin Pop
  @rtype: str
1143 58885d79 Iustin Pop
  @return: a sha1 hexdigest of a block of 64 random bytes
1144 58885d79 Iustin Pop

1145 33081d90 Iustin Pop
  """
1146 33081d90 Iustin Pop
  return sha.new(os.urandom(64)).hexdigest()
1147 33081d90 Iustin Pop
1148 33081d90 Iustin Pop
1149 ca0aa6d0 Michael Hanselmann
def ReadFile(file_name, size=None):
1150 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1151 ca0aa6d0 Michael Hanselmann

1152 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1153 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1154 58885d79 Iustin Pop
  @rtype: str
1155 58885d79 Iustin Pop
  @return: the (possibly partial) conent of the file
1156 ca0aa6d0 Michael Hanselmann

1157 ca0aa6d0 Michael Hanselmann
  """
1158 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1159 ca0aa6d0 Michael Hanselmann
  try:
1160 ca0aa6d0 Michael Hanselmann
    if size is None:
1161 ca0aa6d0 Michael Hanselmann
      return f.read()
1162 ca0aa6d0 Michael Hanselmann
    else:
1163 ca0aa6d0 Michael Hanselmann
      return f.read(size)
1164 ca0aa6d0 Michael Hanselmann
  finally:
1165 ca0aa6d0 Michael Hanselmann
    f.close()
1166 ca0aa6d0 Michael Hanselmann
1167 ca0aa6d0 Michael Hanselmann
1168 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1169 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1170 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1171 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1172 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1173 087b34fe Iustin Pop
  """(Over)write a file atomically.
1174 087b34fe Iustin Pop

1175 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1176 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1177 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1178 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1179 087b34fe Iustin Pop
  mtime/atime of the file.
1180 087b34fe Iustin Pop

1181 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1182 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
1183 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1184 087b34fe Iustin Pop
  temporary file should be removed.
1185 087b34fe Iustin Pop

1186 58885d79 Iustin Pop
  @type file_name: str
1187 58885d79 Iustin Pop
  @param file_name: the target filename
1188 58885d79 Iustin Pop
  @type fn: callable
1189 58885d79 Iustin Pop
  @param fn: content writing function, called with
1190 58885d79 Iustin Pop
      file descriptor as parameter
1191 58885d79 Iustin Pop
  @type data: sr
1192 58885d79 Iustin Pop
  @param data: contents of the file
1193 58885d79 Iustin Pop
  @type mode: int
1194 58885d79 Iustin Pop
  @param mode: file mode
1195 58885d79 Iustin Pop
  @type uid: int
1196 58885d79 Iustin Pop
  @param uid: the owner of the file
1197 58885d79 Iustin Pop
  @type gid: int
1198 58885d79 Iustin Pop
  @param gid: the group of the file
1199 58885d79 Iustin Pop
  @type atime: int
1200 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1201 58885d79 Iustin Pop
  @type mtime: int
1202 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1203 58885d79 Iustin Pop
  @type close: boolean
1204 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1205 58885d79 Iustin Pop
  @type prewrite: callable
1206 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1207 58885d79 Iustin Pop
  @type postwrite: callable
1208 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1209 58885d79 Iustin Pop

1210 58885d79 Iustin Pop
  @rtype: None or int
1211 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1212 58885d79 Iustin Pop
      otherwise the file descriptor
1213 58885d79 Iustin Pop

1214 58885d79 Iustin Pop
  @raise errors.ProgrammerError: if an of the arguments are not valid
1215 71714516 Michael Hanselmann

1216 087b34fe Iustin Pop
  """
1217 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1218 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1219 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1220 087b34fe Iustin Pop
1221 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1222 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1223 087b34fe Iustin Pop
1224 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1225 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1226 087b34fe Iustin Pop
                                 " set or None")
1227 087b34fe Iustin Pop
1228 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1229 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1230 087b34fe Iustin Pop
1231 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1232 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1233 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1234 087b34fe Iustin Pop
  # leaves it in place
1235 087b34fe Iustin Pop
  try:
1236 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1237 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1238 087b34fe Iustin Pop
    if mode:
1239 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1240 71714516 Michael Hanselmann
    if callable(prewrite):
1241 71714516 Michael Hanselmann
      prewrite(fd)
1242 087b34fe Iustin Pop
    if data is not None:
1243 087b34fe Iustin Pop
      os.write(fd, data)
1244 087b34fe Iustin Pop
    else:
1245 087b34fe Iustin Pop
      fn(fd)
1246 71714516 Michael Hanselmann
    if callable(postwrite):
1247 71714516 Michael Hanselmann
      postwrite(fd)
1248 087b34fe Iustin Pop
    os.fsync(fd)
1249 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1250 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1251 70f4497c Michael Hanselmann
    if not dry_run:
1252 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1253 087b34fe Iustin Pop
  finally:
1254 71714516 Michael Hanselmann
    if close:
1255 71714516 Michael Hanselmann
      os.close(fd)
1256 71714516 Michael Hanselmann
      result = None
1257 71714516 Michael Hanselmann
    else:
1258 71714516 Michael Hanselmann
      result = fd
1259 087b34fe Iustin Pop
    RemoveFile(new_name)
1260 78feb6fb Guido Trotter
1261 71714516 Michael Hanselmann
  return result
1262 71714516 Michael Hanselmann
1263 78feb6fb Guido Trotter
1264 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1265 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1266 7b4126b7 Iustin Pop

1267 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1268 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1269 7b4126b7 Iustin Pop
  value, the index will be returned.
1270 7b4126b7 Iustin Pop

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

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

1276 58885d79 Iustin Pop
  @type seq: sequence
1277 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1278 58885d79 Iustin Pop
  @type base: int
1279 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1280 58885d79 Iustin Pop
  @rtype: int
1281 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1282 7b4126b7 Iustin Pop

1283 7b4126b7 Iustin Pop
  """
1284 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1285 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1286 7b4126b7 Iustin Pop
    if elem > idx + base:
1287 7b4126b7 Iustin Pop
      # idx is not used
1288 7b4126b7 Iustin Pop
      return idx + base
1289 7b4126b7 Iustin Pop
  return None
1290 7b4126b7 Iustin Pop
1291 7b4126b7 Iustin Pop
1292 78feb6fb Guido Trotter
def all(seq, pred=bool):
1293 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1294 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
1295 78feb6fb Guido Trotter
    return False
1296 78feb6fb Guido Trotter
  return True
1297 78feb6fb Guido Trotter
1298 78feb6fb Guido Trotter
1299 78feb6fb Guido Trotter
def any(seq, pred=bool):
1300 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1301 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
1302 78feb6fb Guido Trotter
    return True
1303 78feb6fb Guido Trotter
  return False
1304 f7414041 Michael Hanselmann
1305 f7414041 Michael Hanselmann
1306 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1307 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1308 f7414041 Michael Hanselmann

1309 f7414041 Michael Hanselmann
  Element order is preserved.
1310 58885d79 Iustin Pop

1311 58885d79 Iustin Pop
  @type seq: sequence
1312 58885d79 Iustin Pop
  @param seq: the sequence with the source elementes
1313 58885d79 Iustin Pop
  @rtype: list
1314 58885d79 Iustin Pop
  @return: list of unique elements from seq
1315 58885d79 Iustin Pop

1316 f7414041 Michael Hanselmann
  """
1317 f7414041 Michael Hanselmann
  seen = set()
1318 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1319 1862d460 Alexander Schreiber
1320 1862d460 Alexander Schreiber
1321 1862d460 Alexander Schreiber
def IsValidMac(mac):
1322 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1323 1862d460 Alexander Schreiber

1324 1862d460 Alexander Schreiber
  Checks wether the supplied MAC address is formally correct, only
1325 1862d460 Alexander Schreiber
  accepts colon separated format.
1326 58885d79 Iustin Pop

1327 58885d79 Iustin Pop
  @type mac: str
1328 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1329 58885d79 Iustin Pop
  @rtype: boolean
1330 58885d79 Iustin Pop
  @return: True is the MAC seems valid
1331 58885d79 Iustin Pop

1332 1862d460 Alexander Schreiber
  """
1333 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1334 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1335 06009e27 Iustin Pop
1336 06009e27 Iustin Pop
1337 06009e27 Iustin Pop
def TestDelay(duration):
1338 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1339 06009e27 Iustin Pop

1340 58885d79 Iustin Pop
  @type duration: float
1341 58885d79 Iustin Pop
  @param duration: the sleep duration
1342 58885d79 Iustin Pop
  @rtype: boolean
1343 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1344 58885d79 Iustin Pop

1345 06009e27 Iustin Pop
  """
1346 06009e27 Iustin Pop
  if duration < 0:
1347 06009e27 Iustin Pop
    return False
1348 06009e27 Iustin Pop
  time.sleep(duration)
1349 06009e27 Iustin Pop
  return True
1350 8f765069 Iustin Pop
1351 8f765069 Iustin Pop
1352 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1353 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1354 8f765069 Iustin Pop

1355 7d88772a Iustin Pop
  @type fd: int
1356 7d88772a Iustin Pop
  @param fd: the file descriptor
1357 7d88772a Iustin Pop
  @type retries: int
1358 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1359 7d88772a Iustin Pop
      other error than EBADF
1360 7d88772a Iustin Pop

1361 7d88772a Iustin Pop
  """
1362 7d88772a Iustin Pop
  try:
1363 7d88772a Iustin Pop
    os.close(fd)
1364 7d88772a Iustin Pop
  except OSError, err:
1365 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1366 7d88772a Iustin Pop
      if retries > 0:
1367 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1368 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1369 7d88772a Iustin Pop
    # ignore this and go on
1370 7d88772a Iustin Pop
1371 7d88772a Iustin Pop
1372 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1373 7d88772a Iustin Pop
  """Close file descriptors.
1374 7d88772a Iustin Pop

1375 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1376 7d88772a Iustin Pop
  stdin/out/err).
1377 8f765069 Iustin Pop

1378 58885d79 Iustin Pop
  @type noclose_fds: list or None
1379 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1380 58885d79 Iustin Pop
      that should not be closed
1381 58885d79 Iustin Pop

1382 8f765069 Iustin Pop
  """
1383 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1384 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1385 8f765069 Iustin Pop
    try:
1386 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1387 8f765069 Iustin Pop
      if MAXFD < 0:
1388 8f765069 Iustin Pop
        MAXFD = 1024
1389 8f765069 Iustin Pop
    except OSError:
1390 8f765069 Iustin Pop
      MAXFD = 1024
1391 8f765069 Iustin Pop
  else:
1392 8f765069 Iustin Pop
    MAXFD = 1024
1393 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1394 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1395 7d88772a Iustin Pop
    maxfd = MAXFD
1396 7d88772a Iustin Pop
1397 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1398 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1399 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1400 7d88772a Iustin Pop
      continue
1401 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1402 7d88772a Iustin Pop
1403 7d88772a Iustin Pop
1404 7d88772a Iustin Pop
def Daemonize(logfile):
1405 7d88772a Iustin Pop
  """Daemonize the current process.
1406 7d88772a Iustin Pop

1407 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1408 7d88772a Iustin Pop
  runs it in the background as a daemon.
1409 7d88772a Iustin Pop

1410 7d88772a Iustin Pop
  @type logfile: str
1411 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1412 7d88772a Iustin Pop
  @rtype: int
1413 7d88772a Iustin Pop
  @returns: the value zero
1414 7d88772a Iustin Pop

1415 7d88772a Iustin Pop
  """
1416 7d88772a Iustin Pop
  UMASK = 077
1417 7d88772a Iustin Pop
  WORKDIR = "/"
1418 8f765069 Iustin Pop
1419 8f765069 Iustin Pop
  # this might fail
1420 8f765069 Iustin Pop
  pid = os.fork()
1421 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1422 8f765069 Iustin Pop
    os.setsid()
1423 8f765069 Iustin Pop
    # this might fail
1424 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1425 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1426 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1427 8f765069 Iustin Pop
      os.umask(UMASK)
1428 8f765069 Iustin Pop
    else:
1429 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1430 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1431 8f765069 Iustin Pop
  else:
1432 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1433 8f765069 Iustin Pop
1434 7d88772a Iustin Pop
  for fd in range(3):
1435 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1436 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1437 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1438 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1439 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1440 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1441 7d88772a Iustin Pop
  os.dup2(1, 2)
1442 8f765069 Iustin Pop
  return 0
1443 57c177af Iustin Pop
1444 57c177af Iustin Pop
1445 53beffbb Iustin Pop
def DaemonPidFileName(name):
1446 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1447 58885d79 Iustin Pop

1448 58885d79 Iustin Pop
  @type name: str
1449 58885d79 Iustin Pop
  @param name: the daemon name
1450 58885d79 Iustin Pop
  @rtype: str
1451 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1452 58885d79 Iustin Pop
      daemon name
1453 b330ac0b Guido Trotter

1454 b330ac0b Guido Trotter
  """
1455 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1456 b330ac0b Guido Trotter
1457 b330ac0b Guido Trotter
1458 b330ac0b Guido Trotter
def WritePidFile(name):
1459 b330ac0b Guido Trotter
  """Write the current process pidfile.
1460 b330ac0b Guido Trotter

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

1463 58885d79 Iustin Pop
  @type name: str
1464 58885d79 Iustin Pop
  @param name: the daemon name to use
1465 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1466 58885d79 Iustin Pop
      points to a live process
1467 b330ac0b Guido Trotter

1468 b330ac0b Guido Trotter
  """
1469 b330ac0b Guido Trotter
  pid = os.getpid()
1470 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1471 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1472 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1473 b330ac0b Guido Trotter
1474 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1475 b330ac0b Guido Trotter
1476 b330ac0b Guido Trotter
1477 b330ac0b Guido Trotter
def RemovePidFile(name):
1478 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1479 b330ac0b Guido Trotter

1480 b330ac0b Guido Trotter
  Any errors are ignored.
1481 b330ac0b Guido Trotter

1482 58885d79 Iustin Pop
  @type name: str
1483 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1484 58885d79 Iustin Pop

1485 b330ac0b Guido Trotter
  """
1486 b330ac0b Guido Trotter
  pid = os.getpid()
1487 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1488 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1489 b330ac0b Guido Trotter
  try:
1490 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1491 b330ac0b Guido Trotter
  except:
1492 b330ac0b Guido Trotter
    pass
1493 b330ac0b Guido Trotter
1494 b330ac0b Guido Trotter
1495 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1496 ff5251bc Iustin Pop
                waitpid=False):
1497 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1498 b2a1f511 Iustin Pop

1499 b2a1f511 Iustin Pop
  @type pid: int
1500 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1501 38206f3c Iustin Pop
  @type signal_: int
1502 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1503 b2a1f511 Iustin Pop
  @type timeout: int
1504 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1505 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1506 b2a1f511 Iustin Pop
                  will be done
1507 ff5251bc Iustin Pop
  @type waitpid: boolean
1508 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1509 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1510 ff5251bc Iustin Pop
      would remain as zombie
1511 b2a1f511 Iustin Pop

1512 b2a1f511 Iustin Pop
  """
1513 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1514 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1515 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1516 ff5251bc Iustin Pop
    if wait:
1517 ff5251bc Iustin Pop
      try:
1518 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1519 ff5251bc Iustin Pop
      except OSError:
1520 ff5251bc Iustin Pop
        pass
1521 ff5251bc Iustin Pop
1522 b2a1f511 Iustin Pop
  if pid <= 0:
1523 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1524 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1525 b2a1f511 Iustin Pop
1526 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1527 b2a1f511 Iustin Pop
    return
1528 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1529 b2a1f511 Iustin Pop
  if timeout <= 0:
1530 b2a1f511 Iustin Pop
    return
1531 7167159a Michael Hanselmann
1532 7167159a Michael Hanselmann
  # Wait up to $timeout seconds
1533 b2a1f511 Iustin Pop
  end = time.time() + timeout
1534 7167159a Michael Hanselmann
  wait = 0.01
1535 b2a1f511 Iustin Pop
  while time.time() < end and IsProcessAlive(pid):
1536 7167159a Michael Hanselmann
    try:
1537 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1538 7167159a Michael Hanselmann
      if result_pid > 0:
1539 7167159a Michael Hanselmann
        break
1540 7167159a Michael Hanselmann
    except OSError:
1541 7167159a Michael Hanselmann
      pass
1542 7167159a Michael Hanselmann
    time.sleep(wait)
1543 7167159a Michael Hanselmann
    # Make wait time longer for next try
1544 7167159a Michael Hanselmann
    if wait < 0.1:
1545 7167159a Michael Hanselmann
      wait *= 1.5
1546 7167159a Michael Hanselmann
1547 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1548 7167159a Michael Hanselmann
    # Kill process if it's still alive
1549 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1550 b2a1f511 Iustin Pop
1551 b2a1f511 Iustin Pop
1552 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1553 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1554 57c177af Iustin Pop

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

1558 58885d79 Iustin Pop
  @type name: str
1559 58885d79 Iustin Pop
  @param name: the name to look for
1560 58885d79 Iustin Pop
  @type search_path: str
1561 58885d79 Iustin Pop
  @param search_path: location to start at
1562 58885d79 Iustin Pop
  @type test: callable
1563 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1564 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1565 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1566 58885d79 Iustin Pop
  @rtype: str or None
1567 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1568 57c177af Iustin Pop

1569 57c177af Iustin Pop
  """
1570 57c177af Iustin Pop
  for dir_name in search_path:
1571 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1572 57c177af Iustin Pop
    if test(item_name):
1573 57c177af Iustin Pop
      return item_name
1574 57c177af Iustin Pop
  return None
1575 8d1a2a64 Michael Hanselmann
1576 8d1a2a64 Michael Hanselmann
1577 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1578 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1579 8d1a2a64 Michael Hanselmann

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

1583 58885d79 Iustin Pop
  @type vglist: dict
1584 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1585 58885d79 Iustin Pop
  @type vgname: str
1586 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1587 58885d79 Iustin Pop
  @type minsize: int
1588 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1589 58885d79 Iustin Pop
  @rtype: None or str
1590 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1591 8d1a2a64 Michael Hanselmann

1592 8d1a2a64 Michael Hanselmann
  """
1593 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1594 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1595 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1596 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1597 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1598 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1599 8d1a2a64 Michael Hanselmann
  return None
1600 7996a135 Iustin Pop
1601 7996a135 Iustin Pop
1602 45bc5e4a Michael Hanselmann
def SplitTime(value):
1603 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1604 739be818 Michael Hanselmann

1605 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1606 45bc5e4a Michael Hanselmann
  @type value: int or float
1607 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1608 739be818 Michael Hanselmann

1609 739be818 Michael Hanselmann
  """
1610 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1611 45bc5e4a Michael Hanselmann
1612 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1613 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1614 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1615 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1616 45bc5e4a Michael Hanselmann
1617 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1618 739be818 Michael Hanselmann
1619 739be818 Michael Hanselmann
1620 739be818 Michael Hanselmann
def MergeTime(timetuple):
1621 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1622 739be818 Michael Hanselmann

1623 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1624 739be818 Michael Hanselmann
  @type timetuple: tuple
1625 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1626 739be818 Michael Hanselmann

1627 739be818 Michael Hanselmann
  """
1628 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1629 739be818 Michael Hanselmann
1630 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1631 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1632 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1633 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1634 739be818 Michael Hanselmann
1635 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1636 739be818 Michael Hanselmann
1637 739be818 Michael Hanselmann
1638 4a8b186a Michael Hanselmann
def GetNodeDaemonPort():
1639 4a8b186a Michael Hanselmann
  """Get the node daemon port for this cluster.
1640 4a8b186a Michael Hanselmann

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

1645 58885d79 Iustin Pop
  @rtype: int
1646 58885d79 Iustin Pop

1647 4a8b186a Michael Hanselmann
  """
1648 4a8b186a Michael Hanselmann
  try:
1649 4a8b186a Michael Hanselmann
    port = socket.getservbyname("ganeti-noded", "tcp")
1650 4a8b186a Michael Hanselmann
  except socket.error:
1651 4a8b186a Michael Hanselmann
    port = constants.DEFAULT_NODED_PORT
1652 4a8b186a Michael Hanselmann
1653 4a8b186a Michael Hanselmann
  return port
1654 4a8b186a Michael Hanselmann
1655 4a8b186a Michael Hanselmann
1656 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1657 d21d09d6 Iustin Pop
                 multithreaded=False):
1658 82d9caef Iustin Pop
  """Configures the logging module.
1659 82d9caef Iustin Pop

1660 58885d79 Iustin Pop
  @type logfile: str
1661 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1662 58885d79 Iustin Pop
  @type debug: boolean
1663 58885d79 Iustin Pop
  @param debug: whether to enable debug messages too or
1664 58885d79 Iustin Pop
      only those at C{INFO} and above level
1665 58885d79 Iustin Pop
  @type stderr_logging: boolean
1666 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1667 58885d79 Iustin Pop
  @type program: str
1668 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1669 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1670 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1671 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1672 58885d79 Iustin Pop
      stderr logging is disabled
1673 58885d79 Iustin Pop

1674 82d9caef Iustin Pop
  """
1675 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1676 d21d09d6 Iustin Pop
  if multithreaded:
1677 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1678 82d9caef Iustin Pop
  if debug:
1679 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1680 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1681 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1682 82d9caef Iustin Pop
1683 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1684 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1685 82d9caef Iustin Pop
1686 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1687 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1688 7d88772a Iustin Pop
    handler.close()
1689 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1690 6346a9e5 Michael Hanselmann
1691 82d9caef Iustin Pop
  if stderr_logging:
1692 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1693 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1694 82d9caef Iustin Pop
    if debug:
1695 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1696 82d9caef Iustin Pop
    else:
1697 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1698 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1699 82d9caef Iustin Pop
1700 82d9caef Iustin Pop
  # this can fail, if the logging directories are not setup or we have
1701 82d9caef Iustin Pop
  # a permisssion problem; in this case, it's best to log but ignore
1702 82d9caef Iustin Pop
  # the error if stderr_logging is True, and if false we re-raise the
1703 82d9caef Iustin Pop
  # exception since otherwise we could run but without any logs at all
1704 82d9caef Iustin Pop
  try:
1705 82d9caef Iustin Pop
    logfile_handler = logging.FileHandler(logfile)
1706 82d9caef Iustin Pop
    logfile_handler.setFormatter(formatter)
1707 82d9caef Iustin Pop
    if debug:
1708 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.DEBUG)
1709 82d9caef Iustin Pop
    else:
1710 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.INFO)
1711 82d9caef Iustin Pop
    root_logger.addHandler(logfile_handler)
1712 d21d09d6 Iustin Pop
  except EnvironmentError:
1713 82d9caef Iustin Pop
    if stderr_logging:
1714 82d9caef Iustin Pop
      logging.exception("Failed to enable logging to file '%s'", logfile)
1715 82d9caef Iustin Pop
    else:
1716 82d9caef Iustin Pop
      # we need to re-raise the exception
1717 82d9caef Iustin Pop
      raise
1718 82d9caef Iustin Pop
1719 82d9caef Iustin Pop
1720 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1721 f65f63ef Iustin Pop
  """Return the last lines from a file.
1722 f65f63ef Iustin Pop

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

1727 f65f63ef Iustin Pop
  @param fname: the file name
1728 f65f63ef Iustin Pop
  @type lines: int
1729 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1730 f65f63ef Iustin Pop

1731 f65f63ef Iustin Pop
  """
1732 f65f63ef Iustin Pop
  fd = open(fname, "r")
1733 f65f63ef Iustin Pop
  try:
1734 f65f63ef Iustin Pop
    fd.seek(0, 2)
1735 f65f63ef Iustin Pop
    pos = fd.tell()
1736 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1737 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1738 f65f63ef Iustin Pop
    raw_data = fd.read()
1739 f65f63ef Iustin Pop
  finally:
1740 f65f63ef Iustin Pop
    fd.close()
1741 f65f63ef Iustin Pop
1742 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1743 f65f63ef Iustin Pop
  return rows[-lines:]
1744 f65f63ef Iustin Pop
1745 f65f63ef Iustin Pop
1746 26f15862 Iustin Pop
def SafeEncode(text):
1747 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1748 26f15862 Iustin Pop

1749 26f15862 Iustin Pop
  This function mangles the input string and returns a version that
1750 26f15862 Iustin Pop
  should be safe to disply/encode as ASCII. To this end, we first
1751 26f15862 Iustin Pop
  convert it to ASCII using the 'backslashreplace' encoding which
1752 26f15862 Iustin Pop
  should get rid of any non-ASCII chars, and then we again encode it
1753 26f15862 Iustin Pop
  via 'string_escape' which converts '\n' into '\\n' so that log
1754 26f15862 Iustin Pop
  messages remain one-line.
1755 26f15862 Iustin Pop

1756 26f15862 Iustin Pop
  @type text: str or unicode
1757 26f15862 Iustin Pop
  @param text: input data
1758 26f15862 Iustin Pop
  @rtype: str
1759 26f15862 Iustin Pop
  @return: a safe version of text
1760 26f15862 Iustin Pop

1761 26f15862 Iustin Pop
  """
1762 26f15862 Iustin Pop
  text = text.encode('ascii', 'backslashreplace')
1763 26f15862 Iustin Pop
  text = text.encode('string_escape')
1764 26f15862 Iustin Pop
  return text
1765 26f15862 Iustin Pop
1766 26f15862 Iustin Pop
1767 7996a135 Iustin Pop
def LockedMethod(fn):
1768 7996a135 Iustin Pop
  """Synchronized object access decorator.
1769 7996a135 Iustin Pop

1770 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1771 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1772 7996a135 Iustin Pop

1773 7996a135 Iustin Pop
  """
1774 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1775 e67bd559 Michael Hanselmann
    if debug_locks:
1776 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1777 e67bd559 Michael Hanselmann
1778 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1779 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1780 7996a135 Iustin Pop
    lock = self._lock
1781 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1782 7996a135 Iustin Pop
    lock.acquire()
1783 7996a135 Iustin Pop
    try:
1784 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1785 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1786 7996a135 Iustin Pop
    finally:
1787 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1788 7996a135 Iustin Pop
      lock.release()
1789 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1790 7996a135 Iustin Pop
    return result
1791 7996a135 Iustin Pop
  return wrapper
1792 eb0f0ce0 Michael Hanselmann
1793 eb0f0ce0 Michael Hanselmann
1794 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1795 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1796 eb0f0ce0 Michael Hanselmann

1797 58885d79 Iustin Pop
  @type fd: int
1798 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1799 58885d79 Iustin Pop

1800 eb0f0ce0 Michael Hanselmann
  """
1801 eb0f0ce0 Michael Hanselmann
  try:
1802 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1803 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1804 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1805 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1806 eb0f0ce0 Michael Hanselmann
    raise
1807 de499029 Michael Hanselmann
1808 de499029 Michael Hanselmann
1809 a87b4824 Michael Hanselmann
class FileLock(object):
1810 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1811 a87b4824 Michael Hanselmann

1812 a87b4824 Michael Hanselmann
  """
1813 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1814 58885d79 Iustin Pop
    """Constructor for FileLock.
1815 58885d79 Iustin Pop

1816 58885d79 Iustin Pop
    This will open the file denoted by the I{filename} argument.
1817 58885d79 Iustin Pop

1818 58885d79 Iustin Pop
    @type filename: str
1819 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1820 58885d79 Iustin Pop

1821 58885d79 Iustin Pop
    """
1822 a87b4824 Michael Hanselmann
    self.filename = filename
1823 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1824 a87b4824 Michael Hanselmann
1825 a87b4824 Michael Hanselmann
  def __del__(self):
1826 a87b4824 Michael Hanselmann
    self.Close()
1827 a87b4824 Michael Hanselmann
1828 a87b4824 Michael Hanselmann
  def Close(self):
1829 58885d79 Iustin Pop
    """Close the file and release the lock.
1830 58885d79 Iustin Pop

1831 58885d79 Iustin Pop
    """
1832 a87b4824 Michael Hanselmann
    if self.fd:
1833 a87b4824 Michael Hanselmann
      self.fd.close()
1834 a87b4824 Michael Hanselmann
      self.fd = None
1835 a87b4824 Michael Hanselmann
1836 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
1837 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
1838 aa74b828 Michael Hanselmann

1839 aa74b828 Michael Hanselmann
    @type flag: int
1840 58885d79 Iustin Pop
    @param flag: operation flag
1841 aa74b828 Michael Hanselmann
    @type blocking: bool
1842 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
1843 aa74b828 Michael Hanselmann
    @type timeout: None or float
1844 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
1845 aa74b828 Michael Hanselmann
                    non-blocking mode).
1846 aa74b828 Michael Hanselmann
    @type errmsg: string
1847 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
1848 aa74b828 Michael Hanselmann

1849 aa74b828 Michael Hanselmann
    """
1850 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1851 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
1852 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
1853 a87b4824 Michael Hanselmann
1854 aa74b828 Michael Hanselmann
    if timeout is not None:
1855 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1856 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
1857 a87b4824 Michael Hanselmann
1858 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
1859 aa74b828 Michael Hanselmann
    elif not blocking:
1860 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1861 aa74b828 Michael Hanselmann
      timeout_end = None
1862 aa74b828 Michael Hanselmann
1863 aa74b828 Michael Hanselmann
    retry = True
1864 aa74b828 Michael Hanselmann
    while retry:
1865 aa74b828 Michael Hanselmann
      try:
1866 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
1867 aa74b828 Michael Hanselmann
        retry = False
1868 aa74b828 Michael Hanselmann
      except IOError, err:
1869 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
1870 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
1871 aa74b828 Michael Hanselmann
            # Wait before trying again
1872 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
1873 aa74b828 Michael Hanselmann
          else:
1874 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
1875 aa74b828 Michael Hanselmann
        else:
1876 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
1877 aa74b828 Michael Hanselmann
          raise
1878 aa74b828 Michael Hanselmann
1879 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
1880 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1881 a87b4824 Michael Hanselmann

1882 58885d79 Iustin Pop
    @type blocking: boolean
1883 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1884 58885d79 Iustin Pop
        can lock the file or return immediately
1885 58885d79 Iustin Pop
    @type timeout: int or None
1886 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1887 58885d79 Iustin Pop
        (in blocking mode)
1888 58885d79 Iustin Pop

1889 a87b4824 Michael Hanselmann
    """
1890 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
1891 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1892 a87b4824 Michael Hanselmann
1893 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
1894 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1895 a87b4824 Michael Hanselmann

1896 58885d79 Iustin Pop
    @type blocking: boolean
1897 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1898 58885d79 Iustin Pop
        can lock the file or return immediately
1899 58885d79 Iustin Pop
    @type timeout: int or None
1900 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1901 58885d79 Iustin Pop
        (in blocking mode)
1902 58885d79 Iustin Pop

1903 a87b4824 Michael Hanselmann
    """
1904 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
1905 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1906 a87b4824 Michael Hanselmann
1907 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
1908 a87b4824 Michael Hanselmann
    """Unlocks the file.
1909 a87b4824 Michael Hanselmann

1910 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
1911 58885d79 Iustin Pop
    operation::
1912 58885d79 Iustin Pop

1913 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
1914 58885d79 Iustin Pop
      operations.
1915 58885d79 Iustin Pop

1916 58885d79 Iustin Pop
    @type blocking: boolean
1917 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1918 58885d79 Iustin Pop
        can lock the file or return immediately
1919 58885d79 Iustin Pop
    @type timeout: int or None
1920 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1921 58885d79 Iustin Pop
        (in blocking mode)
1922 a87b4824 Michael Hanselmann

1923 a87b4824 Michael Hanselmann
    """
1924 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
1925 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
1926 a87b4824 Michael Hanselmann
1927 a87b4824 Michael Hanselmann
1928 de499029 Michael Hanselmann
class SignalHandler(object):
1929 de499029 Michael Hanselmann
  """Generic signal handler class.
1930 de499029 Michael Hanselmann

1931 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
1932 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
1933 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
1934 58885d79 Iustin Pop
  signal was sent.
1935 58885d79 Iustin Pop

1936 58885d79 Iustin Pop
  @type signum: list
1937 58885d79 Iustin Pop
  @ivar signum: the signals we handle
1938 58885d79 Iustin Pop
  @type called: boolean
1939 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
1940 de499029 Michael Hanselmann

1941 de499029 Michael Hanselmann
  """
1942 de499029 Michael Hanselmann
  def __init__(self, signum):
1943 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1944 de499029 Michael Hanselmann

1945 58885d79 Iustin Pop
    @type signum: int or list of ints
1946 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
1947 de499029 Michael Hanselmann

1948 de499029 Michael Hanselmann
    """
1949 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
1950 de499029 Michael Hanselmann
      self.signum = set([signum])
1951 de499029 Michael Hanselmann
    else:
1952 de499029 Michael Hanselmann
      self.signum = set(signum)
1953 de499029 Michael Hanselmann
1954 de499029 Michael Hanselmann
    self.called = False
1955 de499029 Michael Hanselmann
1956 de499029 Michael Hanselmann
    self._previous = {}
1957 de499029 Michael Hanselmann
    try:
1958 de499029 Michael Hanselmann
      for signum in self.signum:
1959 de499029 Michael Hanselmann
        # Setup handler
1960 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
1961 de499029 Michael Hanselmann
        try:
1962 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
1963 de499029 Michael Hanselmann
        except:
1964 de499029 Michael Hanselmann
          # Restore previous handler
1965 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
1966 de499029 Michael Hanselmann
          raise
1967 de499029 Michael Hanselmann
    except:
1968 de499029 Michael Hanselmann
      # Reset all handlers
1969 de499029 Michael Hanselmann
      self.Reset()
1970 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
1971 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
1972 de499029 Michael Hanselmann
      raise
1973 de499029 Michael Hanselmann
1974 de499029 Michael Hanselmann
  def __del__(self):
1975 de499029 Michael Hanselmann
    self.Reset()
1976 de499029 Michael Hanselmann
1977 de499029 Michael Hanselmann
  def Reset(self):
1978 de499029 Michael Hanselmann
    """Restore previous handler.
1979 de499029 Michael Hanselmann

1980 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
1981 58885d79 Iustin Pop

1982 de499029 Michael Hanselmann
    """
1983 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
1984 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
1985 de499029 Michael Hanselmann
      # If successful, remove from dict
1986 de499029 Michael Hanselmann
      del self._previous[signum]
1987 de499029 Michael Hanselmann
1988 de499029 Michael Hanselmann
  def Clear(self):
1989 58885d79 Iustin Pop
    """Unsets the L{called} flag.
1990 de499029 Michael Hanselmann

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

1993 de499029 Michael Hanselmann
    """
1994 de499029 Michael Hanselmann
    self.called = False
1995 de499029 Michael Hanselmann
1996 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
1997 de499029 Michael Hanselmann
    """Actual signal handling function.
1998 de499029 Michael Hanselmann

1999 de499029 Michael Hanselmann
    """
2000 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2001 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2002 de499029 Michael Hanselmann
    self.called = True
2003 a2d2e1a7 Iustin Pop
2004 a2d2e1a7 Iustin Pop
2005 a2d2e1a7 Iustin Pop
class FieldSet(object):
2006 a2d2e1a7 Iustin Pop
  """A simple field set.
2007 a2d2e1a7 Iustin Pop

2008 a2d2e1a7 Iustin Pop
  Among the features are:
2009 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2010 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2011 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2012 a2d2e1a7 Iustin Pop

2013 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2014 a2d2e1a7 Iustin Pop

2015 a2d2e1a7 Iustin Pop
  """
2016 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2017 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2018 a2d2e1a7 Iustin Pop
2019 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2020 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2021 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2022 a2d2e1a7 Iustin Pop
2023 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2024 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2025 a2d2e1a7 Iustin Pop

2026 a2d2e1a7 Iustin Pop
    @type field: str
2027 a2d2e1a7 Iustin Pop
    @param field: the string to match
2028 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2029 a2d2e1a7 Iustin Pop

2030 a2d2e1a7 Iustin Pop
    """
2031 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2032 a2d2e1a7 Iustin Pop
      return m
2033 a2d2e1a7 Iustin Pop
    return False
2034 a2d2e1a7 Iustin Pop
2035 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2036 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2037 a2d2e1a7 Iustin Pop

2038 a2d2e1a7 Iustin Pop
    @type items: list
2039 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2040 a2d2e1a7 Iustin Pop
    @rtype: list
2041 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2042 a2d2e1a7 Iustin Pop

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