Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 7c4d6c7b

History | View | Annotate | Download (56.1 kB)

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

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

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

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

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

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

121 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
122 a8083063 Iustin Pop
  closed.
123 a8083063 Iustin Pop

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

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

183 36117c2b Iustin Pop
  @type  cmd: string or list
184 36117c2b Iustin Pop
  @param cmd: Command to run
185 36117c2b Iustin Pop
  @type env: dict
186 36117c2b Iustin Pop
  @param env: The environment to use
187 36117c2b Iustin Pop
  @type via_shell: bool
188 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
189 8797df43 Iustin Pop
  @type cwd: string
190 8797df43 Iustin Pop
  @param cwd: the working directory for the program
191 36117c2b Iustin Pop
  @rtype: tuple
192 36117c2b Iustin Pop
  @return: (out, err, status)
193 36117c2b Iustin Pop

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

252 36117c2b Iustin Pop
  @type  cmd: string or list
253 36117c2b Iustin Pop
  @param cmd: Command to run
254 36117c2b Iustin Pop
  @type env: dict
255 36117c2b Iustin Pop
  @param env: The environment to use
256 36117c2b Iustin Pop
  @type via_shell: bool
257 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
258 36117c2b Iustin Pop
  @type output: str
259 36117c2b Iustin Pop
  @param output: the filename in which to save the output
260 8797df43 Iustin Pop
  @type cwd: string
261 8797df43 Iustin Pop
  @param cwd: the working directory for the program
262 36117c2b Iustin Pop
  @rtype: int
263 36117c2b Iustin Pop
  @return: the exit status
264 36117c2b Iustin Pop

265 36117c2b Iustin Pop
  """
266 36117c2b Iustin Pop
  fh = open(output, "a")
267 36117c2b Iustin Pop
  try:
268 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
269 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
270 36117c2b Iustin Pop
                             stdout=fh,
271 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
272 8797df43 Iustin Pop
                             close_fds=True, env=env,
273 8797df43 Iustin Pop
                             cwd=cwd)
274 36117c2b Iustin Pop
275 36117c2b Iustin Pop
    child.stdin.close()
276 36117c2b Iustin Pop
    status = child.wait()
277 36117c2b Iustin Pop
  finally:
278 36117c2b Iustin Pop
    fh.close()
279 36117c2b Iustin Pop
  return status
280 a8083063 Iustin Pop
281 a8083063 Iustin Pop
282 a8083063 Iustin Pop
def RemoveFile(filename):
283 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
284 a8083063 Iustin Pop

285 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
286 a8083063 Iustin Pop
  errors are passed.
287 a8083063 Iustin Pop

288 58885d79 Iustin Pop
  @type filename: str
289 58885d79 Iustin Pop
  @param filename: the file to be removed
290 58885d79 Iustin Pop

291 a8083063 Iustin Pop
  """
292 a8083063 Iustin Pop
  try:
293 a8083063 Iustin Pop
    os.unlink(filename)
294 a8083063 Iustin Pop
  except OSError, err:
295 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
296 a8083063 Iustin Pop
      raise
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
299 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
300 6e797216 Michael Hanselmann
  """Renames a file.
301 6e797216 Michael Hanselmann

302 6e797216 Michael Hanselmann
  @type old: string
303 6e797216 Michael Hanselmann
  @param old: Original path
304 6e797216 Michael Hanselmann
  @type new: string
305 6e797216 Michael Hanselmann
  @param new: New path
306 6e797216 Michael Hanselmann
  @type mkdir: bool
307 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
308 6e797216 Michael Hanselmann
  @type mkdir_mode: int
309 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
310 6e797216 Michael Hanselmann

311 6e797216 Michael Hanselmann
  """
312 6e797216 Michael Hanselmann
  try:
313 6e797216 Michael Hanselmann
    return os.rename(old, new)
314 6e797216 Michael Hanselmann
  except OSError, err:
315 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
316 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
317 6e797216 Michael Hanselmann
    # as efficient.
318 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
319 6e797216 Michael Hanselmann
      # Create directory and try again
320 6e797216 Michael Hanselmann
      os.makedirs(os.path.dirname(new), mkdir_mode)
321 6e797216 Michael Hanselmann
      return os.rename(old, new)
322 6e797216 Michael Hanselmann
    raise
323 6e797216 Michael Hanselmann
324 6e797216 Michael Hanselmann
325 a8083063 Iustin Pop
def _FingerprintFile(filename):
326 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
327 a8083063 Iustin Pop

328 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
329 a8083063 Iustin Pop
  instead.
330 a8083063 Iustin Pop

331 58885d79 Iustin Pop
  @type filename: str
332 58885d79 Iustin Pop
  @param filename: the filename to checksum
333 58885d79 Iustin Pop
  @rtype: str
334 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
335 58885d79 Iustin Pop
      of the file
336 a8083063 Iustin Pop

337 a8083063 Iustin Pop
  """
338 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
339 a8083063 Iustin Pop
    return None
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
  f = open(filename)
342 a8083063 Iustin Pop
343 7ffe8fba Carlos Valiente
  fp = sha1()
344 a8083063 Iustin Pop
  while True:
345 a8083063 Iustin Pop
    data = f.read(4096)
346 a8083063 Iustin Pop
    if not data:
347 a8083063 Iustin Pop
      break
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
    fp.update(data)
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
  return fp.hexdigest()
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
def FingerprintFiles(files):
355 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
356 a8083063 Iustin Pop

357 58885d79 Iustin Pop
  @type files: list
358 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
359 58885d79 Iustin Pop
  @rtype: dict
360 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
361 58885d79 Iustin Pop
      existing files
362 a8083063 Iustin Pop

363 a8083063 Iustin Pop
  """
364 a8083063 Iustin Pop
  ret = {}
365 a8083063 Iustin Pop
366 a8083063 Iustin Pop
  for filename in files:
367 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
368 a8083063 Iustin Pop
    if cksum:
369 a8083063 Iustin Pop
      ret[filename] = cksum
370 a8083063 Iustin Pop
371 a8083063 Iustin Pop
  return ret
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
374 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
375 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
376 a8083063 Iustin Pop

377 58885d79 Iustin Pop
  For the given dictionaries I{target} and I{template}, ensure
378 58885d79 Iustin Pop
  I{target} has all the keys from I{template}. Missing keys are added
379 58885d79 Iustin Pop
  with values from template.
380 a8083063 Iustin Pop

381 58885d79 Iustin Pop
  @type target: dict
382 58885d79 Iustin Pop
  @param target: the dictionary to update
383 58885d79 Iustin Pop
  @type template: dict
384 58885d79 Iustin Pop
  @param template: the dictionary holding the default values
385 58885d79 Iustin Pop
  @type logname: str or None
386 58885d79 Iustin Pop
  @param logname: if not None, causes the missing keys to be
387 58885d79 Iustin Pop
      logged with this name
388 a8083063 Iustin Pop

389 a8083063 Iustin Pop
  """
390 a8083063 Iustin Pop
  missing = []
391 a8083063 Iustin Pop
  for k in template:
392 a8083063 Iustin Pop
    if k not in target:
393 a8083063 Iustin Pop
      missing.append(k)
394 a8083063 Iustin Pop
      target[k] = template[k]
395 a8083063 Iustin Pop
396 a8083063 Iustin Pop
  if missing and logname:
397 bb698c1f Iustin Pop
    logging.warning('%s missing keys %s', logname, ', '.join(missing))
398 a8083063 Iustin Pop
399 a8083063 Iustin Pop
400 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
401 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
402 a5728081 Guido Trotter

403 a5728081 Guido Trotter
  @type target: dict
404 a5728081 Guido Trotter
  @param target: the dict to update
405 a5728081 Guido Trotter
  @type key_types: dict
406 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
407 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
408 a5728081 Guido Trotter
  @type allowed_values: list
409 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
410 a5728081 Guido Trotter

411 a5728081 Guido Trotter
  """
412 a5728081 Guido Trotter
  if allowed_values is None:
413 a5728081 Guido Trotter
    allowed_values = []
414 a5728081 Guido Trotter
415 a5728081 Guido Trotter
  for key in target:
416 a5728081 Guido Trotter
    if key not in key_types:
417 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
418 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
419 a5728081 Guido Trotter
420 a5728081 Guido Trotter
    if target[key] in allowed_values:
421 a5728081 Guido Trotter
      continue
422 a5728081 Guido Trotter
423 a5728081 Guido Trotter
    type = key_types[key]
424 a5728081 Guido Trotter
    if type not in constants.ENFORCEABLE_TYPES:
425 a5728081 Guido Trotter
      msg = "'%s' has non-enforceable type %s" % (key, type)
426 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
427 a5728081 Guido Trotter
428 a5728081 Guido Trotter
    if type == constants.VTYPE_STRING:
429 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
430 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
431 a5728081 Guido Trotter
          target[key] = ''
432 a5728081 Guido Trotter
        else:
433 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
434 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
435 a5728081 Guido Trotter
    elif type == constants.VTYPE_BOOL:
436 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
437 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
438 a5728081 Guido Trotter
          target[key] = False
439 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
440 a5728081 Guido Trotter
          target[key] = True
441 a5728081 Guido Trotter
        else:
442 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
443 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
444 a5728081 Guido Trotter
      elif target[key]:
445 a5728081 Guido Trotter
        target[key] = True
446 a5728081 Guido Trotter
      else:
447 a5728081 Guido Trotter
        target[key] = False
448 a5728081 Guido Trotter
    elif type == constants.VTYPE_SIZE:
449 a5728081 Guido Trotter
      try:
450 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
451 a5728081 Guido Trotter
      except errors.UnitParseError, err:
452 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
453 a5728081 Guido Trotter
              (key, target[key], err)
454 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
455 a5728081 Guido Trotter
    elif type == constants.VTYPE_INT:
456 a5728081 Guido Trotter
      try:
457 a5728081 Guido Trotter
        target[key] = int(target[key])
458 a5728081 Guido Trotter
      except (ValueError, TypeError):
459 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
460 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
461 a5728081 Guido Trotter
462 a5728081 Guido Trotter
463 a8083063 Iustin Pop
def IsProcessAlive(pid):
464 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
465 a8083063 Iustin Pop

466 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
467 44bf25ff Iustin Pop
      will be returned as alive
468 58885d79 Iustin Pop
  @type pid: int
469 58885d79 Iustin Pop
  @param pid: the process ID to check
470 58885d79 Iustin Pop
  @rtype: boolean
471 58885d79 Iustin Pop
  @return: True if the process exists
472 a8083063 Iustin Pop

473 a8083063 Iustin Pop
  """
474 d9f311d7 Iustin Pop
  if pid <= 0:
475 d9f311d7 Iustin Pop
    return False
476 d9f311d7 Iustin Pop
477 a8083063 Iustin Pop
  try:
478 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
479 44bf25ff Iustin Pop
    return True
480 44bf25ff Iustin Pop
  except EnvironmentError, err:
481 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
482 a8083063 Iustin Pop
      return False
483 44bf25ff Iustin Pop
    raise
484 a8083063 Iustin Pop
485 a8083063 Iustin Pop
486 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
487 58885d79 Iustin Pop
  """Read a pid from a file.
488 fee80e90 Guido Trotter

489 58885d79 Iustin Pop
  @type  pidfile: string
490 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
491 58885d79 Iustin Pop
  @rtype: int
492 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
493 d9f311d7 Iustin Pop
           otherwise 0
494 fee80e90 Guido Trotter

495 fee80e90 Guido Trotter
  """
496 fee80e90 Guido Trotter
  try:
497 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
498 d9f311d7 Iustin Pop
  except EnvironmentError, err:
499 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
500 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
501 d9f311d7 Iustin Pop
    return 0
502 fee80e90 Guido Trotter
503 fee80e90 Guido Trotter
  try:
504 fee80e90 Guido Trotter
    pid = int(pf.read())
505 d9f311d7 Iustin Pop
  except ValueError, err:
506 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
507 d9f311d7 Iustin Pop
    return 0
508 fee80e90 Guido Trotter
509 d9f311d7 Iustin Pop
  return pid
510 fee80e90 Guido Trotter
511 fee80e90 Guido Trotter
512 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
513 a8083063 Iustin Pop
  """Try to match a name against a list.
514 a8083063 Iustin Pop

515 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
516 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
517 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
518 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
519 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
520 58885d79 Iustin Pop
  'test1.example.org']}).
521 a8083063 Iustin Pop

522 58885d79 Iustin Pop
  @type key: str
523 58885d79 Iustin Pop
  @param key: the name to be searched
524 58885d79 Iustin Pop
  @type name_list: list
525 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
526 a8083063 Iustin Pop

527 58885d79 Iustin Pop
  @rtype: None or str
528 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
529 58885d79 Iustin Pop
      otherwise the element from the list which matches
530 a8083063 Iustin Pop

531 a8083063 Iustin Pop
  """
532 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
533 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
534 a8083063 Iustin Pop
  if len(names_filtered) != 1:
535 a8083063 Iustin Pop
    return None
536 a8083063 Iustin Pop
  return names_filtered[0]
537 a8083063 Iustin Pop
538 a8083063 Iustin Pop
539 bcf043c9 Iustin Pop
class HostInfo:
540 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
541 bcf043c9 Iustin Pop

542 bcf043c9 Iustin Pop
  """
543 89e1fc26 Iustin Pop
  def __init__(self, name=None):
544 bcf043c9 Iustin Pop
    """Initialize the host name object.
545 bcf043c9 Iustin Pop

546 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
547 89e1fc26 Iustin Pop
    name.
548 bcf043c9 Iustin Pop

549 bcf043c9 Iustin Pop
    """
550 89e1fc26 Iustin Pop
    if name is None:
551 89e1fc26 Iustin Pop
      name = self.SysName()
552 89e1fc26 Iustin Pop
553 89e1fc26 Iustin Pop
    self.query = name
554 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
555 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
556 bcf043c9 Iustin Pop
557 c8a0948f Michael Hanselmann
  def ShortName(self):
558 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
559 c8a0948f Michael Hanselmann

560 c8a0948f Michael Hanselmann
    """
561 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
562 c8a0948f Michael Hanselmann
563 89e1fc26 Iustin Pop
  @staticmethod
564 89e1fc26 Iustin Pop
  def SysName():
565 89e1fc26 Iustin Pop
    """Return the current system's name.
566 bcf043c9 Iustin Pop

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

569 89e1fc26 Iustin Pop
    """
570 89e1fc26 Iustin Pop
    return socket.gethostname()
571 a8083063 Iustin Pop
572 89e1fc26 Iustin Pop
  @staticmethod
573 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
574 89e1fc26 Iustin Pop
    """Look up hostname
575 a8083063 Iustin Pop

576 58885d79 Iustin Pop
    @type hostname: str
577 58885d79 Iustin Pop
    @param hostname: hostname to look up
578 89e1fc26 Iustin Pop

579 58885d79 Iustin Pop
    @rtype: tuple
580 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
581 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
582 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
583 89e1fc26 Iustin Pop

584 89e1fc26 Iustin Pop
    """
585 89e1fc26 Iustin Pop
    try:
586 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
587 89e1fc26 Iustin Pop
    except socket.gaierror, err:
588 89e1fc26 Iustin Pop
      # hostname not found in DNS
589 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
590 a8083063 Iustin Pop
591 89e1fc26 Iustin Pop
    return result
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
594 a8083063 Iustin Pop
def ListVolumeGroups():
595 a8083063 Iustin Pop
  """List volume groups and their size
596 a8083063 Iustin Pop

597 58885d79 Iustin Pop
  @rtype: dict
598 58885d79 Iustin Pop
  @return:
599 58885d79 Iustin Pop
       Dictionary with keys volume name and values
600 58885d79 Iustin Pop
       the size of the volume
601 a8083063 Iustin Pop

602 a8083063 Iustin Pop
  """
603 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
604 a8083063 Iustin Pop
  result = RunCmd(command)
605 a8083063 Iustin Pop
  retval = {}
606 a8083063 Iustin Pop
  if result.failed:
607 a8083063 Iustin Pop
    return retval
608 a8083063 Iustin Pop
609 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
610 a8083063 Iustin Pop
    try:
611 a8083063 Iustin Pop
      name, size = line.split()
612 a8083063 Iustin Pop
      size = int(float(size))
613 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
614 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
615 a8083063 Iustin Pop
      continue
616 a8083063 Iustin Pop
617 a8083063 Iustin Pop
    retval[name] = size
618 a8083063 Iustin Pop
619 a8083063 Iustin Pop
  return retval
620 a8083063 Iustin Pop
621 a8083063 Iustin Pop
622 a8083063 Iustin Pop
def BridgeExists(bridge):
623 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
624 a8083063 Iustin Pop

625 58885d79 Iustin Pop
  @type bridge: str
626 58885d79 Iustin Pop
  @param bridge: the bridge name to check
627 58885d79 Iustin Pop
  @rtype: boolean
628 58885d79 Iustin Pop
  @return: True if it does
629 a8083063 Iustin Pop

630 a8083063 Iustin Pop
  """
631 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
632 a8083063 Iustin Pop
633 a8083063 Iustin Pop
634 a8083063 Iustin Pop
def NiceSort(name_list):
635 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
636 a8083063 Iustin Pop

637 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
638 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
639 58885d79 Iustin Pop
  'a11']}.
640 a8083063 Iustin Pop

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

645 58885d79 Iustin Pop
  @type name_list: list
646 58885d79 Iustin Pop
  @param name_list: the names to be sorted
647 58885d79 Iustin Pop
  @rtype: list
648 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
649 a8083063 Iustin Pop

650 a8083063 Iustin Pop
  """
651 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
652 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
653 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
654 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
655 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
656 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
657 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
658 a8083063 Iustin Pop
  def _TryInt(val):
659 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
660 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
661 a8083063 Iustin Pop
      return val
662 a8083063 Iustin Pop
    rval = int(val)
663 a8083063 Iustin Pop
    return rval
664 a8083063 Iustin Pop
665 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
666 a8083063 Iustin Pop
             for name in name_list]
667 a8083063 Iustin Pop
  to_sort.sort()
668 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
669 a8083063 Iustin Pop
670 a8083063 Iustin Pop
671 a8083063 Iustin Pop
def TryConvert(fn, val):
672 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
673 a8083063 Iustin Pop

674 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
675 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
676 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
677 58885d79 Iustin Pop
  exceptions are propagated to the caller.
678 58885d79 Iustin Pop

679 58885d79 Iustin Pop
  @type fn: callable
680 58885d79 Iustin Pop
  @param fn: function to apply to the value
681 58885d79 Iustin Pop
  @param val: the value to be converted
682 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
683 58885d79 Iustin Pop
      otherwise the original value.
684 a8083063 Iustin Pop

685 a8083063 Iustin Pop
  """
686 a8083063 Iustin Pop
  try:
687 a8083063 Iustin Pop
    nv = fn(val)
688 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
689 a8083063 Iustin Pop
    nv = val
690 a8083063 Iustin Pop
  return nv
691 a8083063 Iustin Pop
692 a8083063 Iustin Pop
693 a8083063 Iustin Pop
def IsValidIP(ip):
694 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
695 a8083063 Iustin Pop

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

699 58885d79 Iustin Pop
  @type ip: str
700 58885d79 Iustin Pop
  @param ip: the address to be checked
701 58885d79 Iustin Pop
  @rtype: a regular expression match object
702 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
703 58885d79 Iustin Pop
      address is not valid
704 a8083063 Iustin Pop

705 a8083063 Iustin Pop
  """
706 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
707 58885d79 Iustin Pop
  #TODO: convert and return only boolean
708 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
709 a8083063 Iustin Pop
710 a8083063 Iustin Pop
711 a8083063 Iustin Pop
def IsValidShellParam(word):
712 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
713 a8083063 Iustin Pop

714 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
715 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
716 a8083063 Iustin Pop
  the actual command.
717 a8083063 Iustin Pop

718 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
719 a8083063 Iustin Pop
  side.
720 a8083063 Iustin Pop

721 58885d79 Iustin Pop
  @type word: str
722 58885d79 Iustin Pop
  @param word: the word to check
723 58885d79 Iustin Pop
  @rtype: boolean
724 58885d79 Iustin Pop
  @return: True if the word is 'safe'
725 58885d79 Iustin Pop

726 a8083063 Iustin Pop
  """
727 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
730 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
731 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
732 a8083063 Iustin Pop

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

738 58885d79 Iustin Pop
  @type template: str
739 58885d79 Iustin Pop
  @param template: the string holding the template for the
740 58885d79 Iustin Pop
      string formatting
741 58885d79 Iustin Pop
  @rtype: str
742 58885d79 Iustin Pop
  @return: the expanded command line
743 58885d79 Iustin Pop

744 a8083063 Iustin Pop
  """
745 a8083063 Iustin Pop
  for word in args:
746 a8083063 Iustin Pop
    if not IsValidShellParam(word):
747 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
748 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
749 a8083063 Iustin Pop
  return template % args
750 a8083063 Iustin Pop
751 a8083063 Iustin Pop
752 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
753 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
754 a8083063 Iustin Pop

755 58885d79 Iustin Pop
  @type value: int
756 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
757 9fbfbb7b Iustin Pop
  @type units: char
758 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
759 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
760 9fbfbb7b Iustin Pop
      - 'm' for MiBs
761 9fbfbb7b Iustin Pop
      - 'g' for GiBs
762 9fbfbb7b Iustin Pop
      - 't' for TiBs
763 58885d79 Iustin Pop
  @rtype: str
764 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
765 a8083063 Iustin Pop

766 a8083063 Iustin Pop
  """
767 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
768 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
769 a8083063 Iustin Pop
770 9fbfbb7b Iustin Pop
  suffix = ''
771 9fbfbb7b Iustin Pop
772 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
773 9fbfbb7b Iustin Pop
    if units == 'h':
774 9fbfbb7b Iustin Pop
      suffix = 'M'
775 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
776 9fbfbb7b Iustin Pop
777 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
778 9fbfbb7b Iustin Pop
    if units == 'h':
779 9fbfbb7b Iustin Pop
      suffix = 'G'
780 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
781 a8083063 Iustin Pop
782 a8083063 Iustin Pop
  else:
783 9fbfbb7b Iustin Pop
    if units == 'h':
784 9fbfbb7b Iustin Pop
      suffix = 'T'
785 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
786 a8083063 Iustin Pop
787 a8083063 Iustin Pop
788 a8083063 Iustin Pop
def ParseUnit(input_string):
789 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
790 a8083063 Iustin Pop

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

795 a8083063 Iustin Pop
  """
796 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
797 a8083063 Iustin Pop
  if not m:
798 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
799 a8083063 Iustin Pop
800 a8083063 Iustin Pop
  value = float(m.groups()[0])
801 a8083063 Iustin Pop
802 a8083063 Iustin Pop
  unit = m.groups()[1]
803 a8083063 Iustin Pop
  if unit:
804 a8083063 Iustin Pop
    lcunit = unit.lower()
805 a8083063 Iustin Pop
  else:
806 a8083063 Iustin Pop
    lcunit = 'm'
807 a8083063 Iustin Pop
808 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
809 a8083063 Iustin Pop
    # Value already in MiB
810 a8083063 Iustin Pop
    pass
811 a8083063 Iustin Pop
812 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
813 a8083063 Iustin Pop
    value *= 1024
814 a8083063 Iustin Pop
815 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
816 a8083063 Iustin Pop
    value *= 1024 * 1024
817 a8083063 Iustin Pop
818 a8083063 Iustin Pop
  else:
819 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
820 a8083063 Iustin Pop
821 a8083063 Iustin Pop
  # Make sure we round up
822 a8083063 Iustin Pop
  if int(value) < value:
823 a8083063 Iustin Pop
    value += 1
824 a8083063 Iustin Pop
825 a8083063 Iustin Pop
  # Round up to the next multiple of 4
826 a8083063 Iustin Pop
  value = int(value)
827 a8083063 Iustin Pop
  if value % 4:
828 a8083063 Iustin Pop
    value += 4 - value % 4
829 a8083063 Iustin Pop
830 a8083063 Iustin Pop
  return value
831 a8083063 Iustin Pop
832 a8083063 Iustin Pop
833 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
834 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
835 a8083063 Iustin Pop

836 58885d79 Iustin Pop
  @type file_name: str
837 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
838 58885d79 Iustin Pop
  @type key: str
839 58885d79 Iustin Pop
  @param key: string containing key
840 58885d79 Iustin Pop

841 a8083063 Iustin Pop
  """
842 a8083063 Iustin Pop
  key_fields = key.split()
843 a8083063 Iustin Pop
844 a8083063 Iustin Pop
  f = open(file_name, 'a+')
845 a8083063 Iustin Pop
  try:
846 a8083063 Iustin Pop
    nl = True
847 a8083063 Iustin Pop
    for line in f:
848 a8083063 Iustin Pop
      # Ignore whitespace changes
849 a8083063 Iustin Pop
      if line.split() == key_fields:
850 a8083063 Iustin Pop
        break
851 a8083063 Iustin Pop
      nl = line.endswith('\n')
852 a8083063 Iustin Pop
    else:
853 a8083063 Iustin Pop
      if not nl:
854 a8083063 Iustin Pop
        f.write("\n")
855 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
856 a8083063 Iustin Pop
      f.write("\n")
857 a8083063 Iustin Pop
      f.flush()
858 a8083063 Iustin Pop
  finally:
859 a8083063 Iustin Pop
    f.close()
860 a8083063 Iustin Pop
861 a8083063 Iustin Pop
862 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
863 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
864 a8083063 Iustin Pop

865 58885d79 Iustin Pop
  @type file_name: str
866 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
867 58885d79 Iustin Pop
  @type key: str
868 58885d79 Iustin Pop
  @param key: string containing key
869 58885d79 Iustin Pop

870 a8083063 Iustin Pop
  """
871 a8083063 Iustin Pop
  key_fields = key.split()
872 a8083063 Iustin Pop
873 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
874 a8083063 Iustin Pop
  try:
875 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
876 a8083063 Iustin Pop
    try:
877 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
878 59f82e3f Michael Hanselmann
      try:
879 59f82e3f Michael Hanselmann
        for line in f:
880 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
881 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
882 59f82e3f Michael Hanselmann
            out.write(line)
883 899d2a81 Michael Hanselmann
884 899d2a81 Michael Hanselmann
        out.flush()
885 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
886 899d2a81 Michael Hanselmann
      finally:
887 899d2a81 Michael Hanselmann
        f.close()
888 899d2a81 Michael Hanselmann
    finally:
889 899d2a81 Michael Hanselmann
      out.close()
890 899d2a81 Michael Hanselmann
  except:
891 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
892 899d2a81 Michael Hanselmann
    raise
893 899d2a81 Michael Hanselmann
894 899d2a81 Michael Hanselmann
895 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
896 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
897 899d2a81 Michael Hanselmann

898 58885d79 Iustin Pop
  @type file_name: str
899 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
900 58885d79 Iustin Pop
  @type ip: str
901 58885d79 Iustin Pop
  @param ip: the IP address
902 58885d79 Iustin Pop
  @type hostname: str
903 58885d79 Iustin Pop
  @param hostname: the hostname to be added
904 58885d79 Iustin Pop
  @type aliases: list
905 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
906 58885d79 Iustin Pop

907 899d2a81 Michael Hanselmann
  """
908 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
909 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
910 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
911 7fbb1f65 Michael Hanselmann
912 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
913 899d2a81 Michael Hanselmann
  try:
914 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
915 9440aeab Michael Hanselmann
    try:
916 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
917 9440aeab Michael Hanselmann
      try:
918 9440aeab Michael Hanselmann
        for line in f:
919 9440aeab Michael Hanselmann
          fields = line.split()
920 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
921 9440aeab Michael Hanselmann
            continue
922 9440aeab Michael Hanselmann
          out.write(line)
923 9440aeab Michael Hanselmann
924 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
925 9440aeab Michael Hanselmann
        if aliases:
926 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
927 9440aeab Michael Hanselmann
        out.write('\n')
928 9440aeab Michael Hanselmann
929 9440aeab Michael Hanselmann
        out.flush()
930 2e3e75b7 Michael Hanselmann
        os.fsync(out)
931 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
932 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
933 9440aeab Michael Hanselmann
      finally:
934 9440aeab Michael Hanselmann
        f.close()
935 9440aeab Michael Hanselmann
    finally:
936 9440aeab Michael Hanselmann
      out.close()
937 9440aeab Michael Hanselmann
  except:
938 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
939 9440aeab Michael Hanselmann
    raise
940 899d2a81 Michael Hanselmann
941 899d2a81 Michael Hanselmann
942 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
943 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
944 d9c02ca6 Michael Hanselmann

945 58885d79 Iustin Pop
  @type hostname: str
946 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
947 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
948 58885d79 Iustin Pop

949 d9c02ca6 Michael Hanselmann
  """
950 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
951 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
952 d9c02ca6 Michael Hanselmann
953 d9c02ca6 Michael Hanselmann
954 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
955 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
956 899d2a81 Michael Hanselmann

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

959 58885d79 Iustin Pop
  @type file_name: str
960 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
961 58885d79 Iustin Pop
  @type hostname: str
962 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
963 58885d79 Iustin Pop

964 899d2a81 Michael Hanselmann
  """
965 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
966 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
967 899d2a81 Michael Hanselmann
  try:
968 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
969 899d2a81 Michael Hanselmann
    try:
970 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
971 899d2a81 Michael Hanselmann
      try:
972 899d2a81 Michael Hanselmann
        for line in f:
973 899d2a81 Michael Hanselmann
          fields = line.split()
974 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
975 899d2a81 Michael Hanselmann
            names = fields[1:]
976 899d2a81 Michael Hanselmann
            if hostname in names:
977 899d2a81 Michael Hanselmann
              while hostname in names:
978 899d2a81 Michael Hanselmann
                names.remove(hostname)
979 899d2a81 Michael Hanselmann
              if names:
980 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
981 899d2a81 Michael Hanselmann
              continue
982 899d2a81 Michael Hanselmann
983 899d2a81 Michael Hanselmann
          out.write(line)
984 59f82e3f Michael Hanselmann
985 59f82e3f Michael Hanselmann
        out.flush()
986 2e3e75b7 Michael Hanselmann
        os.fsync(out)
987 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
988 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
989 59f82e3f Michael Hanselmann
      finally:
990 59f82e3f Michael Hanselmann
        f.close()
991 a8083063 Iustin Pop
    finally:
992 59f82e3f Michael Hanselmann
      out.close()
993 59f82e3f Michael Hanselmann
  except:
994 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
995 59f82e3f Michael Hanselmann
    raise
996 a8083063 Iustin Pop
997 a8083063 Iustin Pop
998 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
999 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1000 d9c02ca6 Michael Hanselmann

1001 58885d79 Iustin Pop
  @type hostname: str
1002 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1003 58885d79 Iustin Pop
      full and shot name will be removed from
1004 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1005 58885d79 Iustin Pop

1006 d9c02ca6 Michael Hanselmann
  """
1007 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1008 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1009 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1010 d9c02ca6 Michael Hanselmann
1011 d9c02ca6 Michael Hanselmann
1012 a8083063 Iustin Pop
def CreateBackup(file_name):
1013 a8083063 Iustin Pop
  """Creates a backup of a file.
1014 a8083063 Iustin Pop

1015 58885d79 Iustin Pop
  @type file_name: str
1016 58885d79 Iustin Pop
  @param file_name: file to be backed up
1017 58885d79 Iustin Pop
  @rtype: str
1018 58885d79 Iustin Pop
  @return: the path to the newly created backup
1019 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1020 a8083063 Iustin Pop

1021 a8083063 Iustin Pop
  """
1022 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1023 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1024 3ecf6786 Iustin Pop
                                file_name)
1025 a8083063 Iustin Pop
1026 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
1027 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1028 081b1e69 Michael Hanselmann
1029 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1030 081b1e69 Michael Hanselmann
  try:
1031 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1032 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1033 081b1e69 Michael Hanselmann
    try:
1034 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1035 081b1e69 Michael Hanselmann
    finally:
1036 081b1e69 Michael Hanselmann
      fdst.close()
1037 081b1e69 Michael Hanselmann
  finally:
1038 081b1e69 Michael Hanselmann
    fsrc.close()
1039 081b1e69 Michael Hanselmann
1040 a8083063 Iustin Pop
  return backup_name
1041 a8083063 Iustin Pop
1042 a8083063 Iustin Pop
1043 a8083063 Iustin Pop
def ShellQuote(value):
1044 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1045 3ecf6786 Iustin Pop

1046 58885d79 Iustin Pop
  @type value: str
1047 58885d79 Iustin Pop
  @param value: the argument to be quoted
1048 58885d79 Iustin Pop
  @rtype: str
1049 58885d79 Iustin Pop
  @return: the quoted value
1050 58885d79 Iustin Pop

1051 a8083063 Iustin Pop
  """
1052 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1053 a8083063 Iustin Pop
    return value
1054 a8083063 Iustin Pop
  else:
1055 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1056 a8083063 Iustin Pop
1057 a8083063 Iustin Pop
1058 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1059 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1060 58885d79 Iustin Pop

1061 58885d79 Iustin Pop
  @type args: list
1062 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1063 58885d79 Iustin Pop
  @rtype: str
1064 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1065 a8083063 Iustin Pop

1066 a8083063 Iustin Pop
  """
1067 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1068 88d14415 Michael Hanselmann
1069 88d14415 Michael Hanselmann
1070 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1071 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1072 2c30e9d7 Alexander Schreiber

1073 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1074 58885d79 Iustin Pop
  to it.
1075 58885d79 Iustin Pop

1076 58885d79 Iustin Pop
  @type target: str
1077 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1078 58885d79 Iustin Pop
  @type port: int
1079 58885d79 Iustin Pop
  @param port: the port to connect to
1080 58885d79 Iustin Pop
  @type timeout: int
1081 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1082 58885d79 Iustin Pop
  @type live_port_needed: boolean
1083 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1084 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1085 58885d79 Iustin Pop
  @type source: str or None
1086 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1087 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1088 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1089 2c30e9d7 Alexander Schreiber

1090 2c30e9d7 Alexander Schreiber
  """
1091 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1092 2c30e9d7 Alexander Schreiber
1093 0b5ad33e Iustin Pop
  success = False
1094 2c30e9d7 Alexander Schreiber
1095 b15d625f Iustin Pop
  if source is not None:
1096 b15d625f Iustin Pop
    try:
1097 b15d625f Iustin Pop
      sock.bind((source, 0))
1098 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1099 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1100 b15d625f Iustin Pop
        success = False
1101 2c30e9d7 Alexander Schreiber
1102 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1103 2c30e9d7 Alexander Schreiber
1104 2c30e9d7 Alexander Schreiber
  try:
1105 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1106 2c30e9d7 Alexander Schreiber
    sock.close()
1107 2c30e9d7 Alexander Schreiber
    success = True
1108 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1109 2c30e9d7 Alexander Schreiber
    success = False
1110 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
1111 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1112 2c30e9d7 Alexander Schreiber
1113 2c30e9d7 Alexander Schreiber
  return success
1114 eedbda4b Michael Hanselmann
1115 eedbda4b Michael Hanselmann
1116 caad16e2 Iustin Pop
def OwnIpAddress(address):
1117 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1118 caad16e2 Iustin Pop

1119 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1120 caad16e2 Iustin Pop
  address.
1121 caad16e2 Iustin Pop

1122 caad16e2 Iustin Pop
  @type address: string
1123 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1124 caad16e2 Iustin Pop
  @rtype: bool
1125 58885d79 Iustin Pop
  @return: True if we own the address
1126 caad16e2 Iustin Pop

1127 caad16e2 Iustin Pop
  """
1128 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1129 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1130 caad16e2 Iustin Pop
1131 caad16e2 Iustin Pop
1132 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1133 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1134 58885d79 Iustin Pop

1135 58885d79 Iustin Pop
  @type path: str
1136 58885d79 Iustin Pop
  @param path: the directory to enumerate
1137 58885d79 Iustin Pop
  @rtype: list
1138 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1139 eedbda4b Michael Hanselmann

1140 eedbda4b Michael Hanselmann
  """
1141 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1142 f3299a07 Michael Hanselmann
  files.sort()
1143 f3299a07 Michael Hanselmann
  return files
1144 2f8b60b3 Iustin Pop
1145 2f8b60b3 Iustin Pop
1146 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1147 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1148 257f4c0a Iustin Pop

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

1153 2f8b60b3 Iustin Pop
  """
1154 2f8b60b3 Iustin Pop
  try:
1155 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1156 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1157 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1158 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1159 257f4c0a Iustin Pop
    else:
1160 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1161 257f4c0a Iustin Pop
                                   type(user))
1162 2f8b60b3 Iustin Pop
  except KeyError:
1163 2f8b60b3 Iustin Pop
    return default
1164 2f8b60b3 Iustin Pop
  return result.pw_dir
1165 59072e7e Michael Hanselmann
1166 59072e7e Michael Hanselmann
1167 24818e8f Michael Hanselmann
def NewUUID():
1168 59072e7e Michael Hanselmann
  """Returns a random UUID.
1169 59072e7e Michael Hanselmann

1170 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1171 58885d79 Iustin Pop
      filesystem.
1172 58885d79 Iustin Pop
  @rtype: str
1173 58885d79 Iustin Pop

1174 59072e7e Michael Hanselmann
  """
1175 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
1176 59072e7e Michael Hanselmann
  try:
1177 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
1178 59072e7e Michael Hanselmann
  finally:
1179 59072e7e Michael Hanselmann
    f.close()
1180 087b34fe Iustin Pop
1181 087b34fe Iustin Pop
1182 33081d90 Iustin Pop
def GenerateSecret():
1183 33081d90 Iustin Pop
  """Generates a random secret.
1184 33081d90 Iustin Pop

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

1188 58885d79 Iustin Pop
  @rtype: str
1189 58885d79 Iustin Pop
  @return: a sha1 hexdigest of a block of 64 random bytes
1190 58885d79 Iustin Pop

1191 33081d90 Iustin Pop
  """
1192 7ffe8fba Carlos Valiente
  return sha1(os.urandom(64)).hexdigest()
1193 33081d90 Iustin Pop
1194 33081d90 Iustin Pop
1195 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1196 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1197 9dae41ad Guido Trotter

1198 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1199 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1200 9dae41ad Guido Trotter

1201 9dae41ad Guido Trotter
  """
1202 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1203 9dae41ad Guido Trotter
    try:
1204 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1205 9dae41ad Guido Trotter
    except EnvironmentError, err:
1206 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1207 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1208 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1209 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1210 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1211 9dae41ad Guido Trotter
1212 9dae41ad Guido Trotter
1213 ca0aa6d0 Michael Hanselmann
def ReadFile(file_name, size=None):
1214 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1215 ca0aa6d0 Michael Hanselmann

1216 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1217 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1218 58885d79 Iustin Pop
  @rtype: str
1219 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1220 ca0aa6d0 Michael Hanselmann

1221 ca0aa6d0 Michael Hanselmann
  """
1222 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1223 ca0aa6d0 Michael Hanselmann
  try:
1224 ca0aa6d0 Michael Hanselmann
    if size is None:
1225 ca0aa6d0 Michael Hanselmann
      return f.read()
1226 ca0aa6d0 Michael Hanselmann
    else:
1227 ca0aa6d0 Michael Hanselmann
      return f.read(size)
1228 ca0aa6d0 Michael Hanselmann
  finally:
1229 ca0aa6d0 Michael Hanselmann
    f.close()
1230 ca0aa6d0 Michael Hanselmann
1231 ca0aa6d0 Michael Hanselmann
1232 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1233 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1234 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1235 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1236 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1237 087b34fe Iustin Pop
  """(Over)write a file atomically.
1238 087b34fe Iustin Pop

1239 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1240 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1241 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1242 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1243 087b34fe Iustin Pop
  mtime/atime of the file.
1244 087b34fe Iustin Pop

1245 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1246 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1247 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1248 087b34fe Iustin Pop
  temporary file should be removed.
1249 087b34fe Iustin Pop

1250 58885d79 Iustin Pop
  @type file_name: str
1251 58885d79 Iustin Pop
  @param file_name: the target filename
1252 58885d79 Iustin Pop
  @type fn: callable
1253 58885d79 Iustin Pop
  @param fn: content writing function, called with
1254 58885d79 Iustin Pop
      file descriptor as parameter
1255 69efe319 Michael Hanselmann
  @type data: str
1256 58885d79 Iustin Pop
  @param data: contents of the file
1257 58885d79 Iustin Pop
  @type mode: int
1258 58885d79 Iustin Pop
  @param mode: file mode
1259 58885d79 Iustin Pop
  @type uid: int
1260 58885d79 Iustin Pop
  @param uid: the owner of the file
1261 58885d79 Iustin Pop
  @type gid: int
1262 58885d79 Iustin Pop
  @param gid: the group of the file
1263 58885d79 Iustin Pop
  @type atime: int
1264 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1265 58885d79 Iustin Pop
  @type mtime: int
1266 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1267 58885d79 Iustin Pop
  @type close: boolean
1268 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1269 58885d79 Iustin Pop
  @type prewrite: callable
1270 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1271 58885d79 Iustin Pop
  @type postwrite: callable
1272 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1273 58885d79 Iustin Pop

1274 58885d79 Iustin Pop
  @rtype: None or int
1275 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1276 58885d79 Iustin Pop
      otherwise the file descriptor
1277 58885d79 Iustin Pop

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

1280 087b34fe Iustin Pop
  """
1281 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1282 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1283 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1284 087b34fe Iustin Pop
1285 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1286 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1287 087b34fe Iustin Pop
1288 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1289 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1290 087b34fe Iustin Pop
                                 " set or None")
1291 087b34fe Iustin Pop
1292 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1293 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1294 087b34fe Iustin Pop
1295 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1296 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1297 81b7354c Iustin Pop
  do_remove = True
1298 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1299 087b34fe Iustin Pop
  # leaves it in place
1300 087b34fe Iustin Pop
  try:
1301 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1302 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1303 087b34fe Iustin Pop
    if mode:
1304 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1305 71714516 Michael Hanselmann
    if callable(prewrite):
1306 71714516 Michael Hanselmann
      prewrite(fd)
1307 087b34fe Iustin Pop
    if data is not None:
1308 087b34fe Iustin Pop
      os.write(fd, data)
1309 087b34fe Iustin Pop
    else:
1310 087b34fe Iustin Pop
      fn(fd)
1311 71714516 Michael Hanselmann
    if callable(postwrite):
1312 71714516 Michael Hanselmann
      postwrite(fd)
1313 087b34fe Iustin Pop
    os.fsync(fd)
1314 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1315 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1316 70f4497c Michael Hanselmann
    if not dry_run:
1317 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1318 81b7354c Iustin Pop
      do_remove = False
1319 087b34fe Iustin Pop
  finally:
1320 71714516 Michael Hanselmann
    if close:
1321 71714516 Michael Hanselmann
      os.close(fd)
1322 71714516 Michael Hanselmann
      result = None
1323 71714516 Michael Hanselmann
    else:
1324 71714516 Michael Hanselmann
      result = fd
1325 81b7354c Iustin Pop
    if do_remove:
1326 81b7354c Iustin Pop
      RemoveFile(new_name)
1327 78feb6fb Guido Trotter
1328 71714516 Michael Hanselmann
  return result
1329 71714516 Michael Hanselmann
1330 78feb6fb Guido Trotter
1331 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1332 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1333 7b4126b7 Iustin Pop

1334 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1335 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1336 7b4126b7 Iustin Pop
  value, the index will be returned.
1337 7b4126b7 Iustin Pop

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

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

1343 58885d79 Iustin Pop
  @type seq: sequence
1344 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1345 58885d79 Iustin Pop
  @type base: int
1346 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1347 58885d79 Iustin Pop
  @rtype: int
1348 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1349 7b4126b7 Iustin Pop

1350 7b4126b7 Iustin Pop
  """
1351 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1352 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1353 7b4126b7 Iustin Pop
    if elem > idx + base:
1354 7b4126b7 Iustin Pop
      # idx is not used
1355 7b4126b7 Iustin Pop
      return idx + base
1356 7b4126b7 Iustin Pop
  return None
1357 7b4126b7 Iustin Pop
1358 7b4126b7 Iustin Pop
1359 78feb6fb Guido Trotter
def all(seq, pred=bool):
1360 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1361 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilterfalse(pred, seq):
1362 78feb6fb Guido Trotter
    return False
1363 78feb6fb Guido Trotter
  return True
1364 78feb6fb Guido Trotter
1365 78feb6fb Guido Trotter
1366 78feb6fb Guido Trotter
def any(seq, pred=bool):
1367 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1368 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilter(pred, seq):
1369 78feb6fb Guido Trotter
    return True
1370 78feb6fb Guido Trotter
  return False
1371 f7414041 Michael Hanselmann
1372 f7414041 Michael Hanselmann
1373 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1374 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1375 f7414041 Michael Hanselmann

1376 f7414041 Michael Hanselmann
  Element order is preserved.
1377 58885d79 Iustin Pop

1378 58885d79 Iustin Pop
  @type seq: sequence
1379 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1380 58885d79 Iustin Pop
  @rtype: list
1381 58885d79 Iustin Pop
  @return: list of unique elements from seq
1382 58885d79 Iustin Pop

1383 f7414041 Michael Hanselmann
  """
1384 f7414041 Michael Hanselmann
  seen = set()
1385 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1386 1862d460 Alexander Schreiber
1387 1862d460 Alexander Schreiber
1388 1862d460 Alexander Schreiber
def IsValidMac(mac):
1389 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1390 1862d460 Alexander Schreiber

1391 5bbd3f7f Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
1392 1862d460 Alexander Schreiber
  accepts colon separated format.
1393 58885d79 Iustin Pop

1394 58885d79 Iustin Pop
  @type mac: str
1395 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1396 58885d79 Iustin Pop
  @rtype: boolean
1397 58885d79 Iustin Pop
  @return: True is the MAC seems valid
1398 58885d79 Iustin Pop

1399 1862d460 Alexander Schreiber
  """
1400 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1401 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1402 06009e27 Iustin Pop
1403 06009e27 Iustin Pop
1404 06009e27 Iustin Pop
def TestDelay(duration):
1405 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1406 06009e27 Iustin Pop

1407 58885d79 Iustin Pop
  @type duration: float
1408 58885d79 Iustin Pop
  @param duration: the sleep duration
1409 58885d79 Iustin Pop
  @rtype: boolean
1410 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1411 58885d79 Iustin Pop

1412 06009e27 Iustin Pop
  """
1413 06009e27 Iustin Pop
  if duration < 0:
1414 06009e27 Iustin Pop
    return False
1415 06009e27 Iustin Pop
  time.sleep(duration)
1416 06009e27 Iustin Pop
  return True
1417 8f765069 Iustin Pop
1418 8f765069 Iustin Pop
1419 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1420 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1421 8f765069 Iustin Pop

1422 7d88772a Iustin Pop
  @type fd: int
1423 7d88772a Iustin Pop
  @param fd: the file descriptor
1424 7d88772a Iustin Pop
  @type retries: int
1425 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1426 7d88772a Iustin Pop
      other error than EBADF
1427 7d88772a Iustin Pop

1428 7d88772a Iustin Pop
  """
1429 7d88772a Iustin Pop
  try:
1430 7d88772a Iustin Pop
    os.close(fd)
1431 7d88772a Iustin Pop
  except OSError, err:
1432 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1433 7d88772a Iustin Pop
      if retries > 0:
1434 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1435 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1436 7d88772a Iustin Pop
    # ignore this and go on
1437 7d88772a Iustin Pop
1438 7d88772a Iustin Pop
1439 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1440 7d88772a Iustin Pop
  """Close file descriptors.
1441 7d88772a Iustin Pop

1442 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1443 7d88772a Iustin Pop
  stdin/out/err).
1444 8f765069 Iustin Pop

1445 58885d79 Iustin Pop
  @type noclose_fds: list or None
1446 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1447 58885d79 Iustin Pop
      that should not be closed
1448 58885d79 Iustin Pop

1449 8f765069 Iustin Pop
  """
1450 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1451 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1452 8f765069 Iustin Pop
    try:
1453 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1454 8f765069 Iustin Pop
      if MAXFD < 0:
1455 8f765069 Iustin Pop
        MAXFD = 1024
1456 8f765069 Iustin Pop
    except OSError:
1457 8f765069 Iustin Pop
      MAXFD = 1024
1458 8f765069 Iustin Pop
  else:
1459 8f765069 Iustin Pop
    MAXFD = 1024
1460 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1461 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1462 7d88772a Iustin Pop
    maxfd = MAXFD
1463 7d88772a Iustin Pop
1464 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1465 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1466 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1467 7d88772a Iustin Pop
      continue
1468 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1469 7d88772a Iustin Pop
1470 7d88772a Iustin Pop
1471 7d88772a Iustin Pop
def Daemonize(logfile):
1472 7d88772a Iustin Pop
  """Daemonize the current process.
1473 7d88772a Iustin Pop

1474 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1475 7d88772a Iustin Pop
  runs it in the background as a daemon.
1476 7d88772a Iustin Pop

1477 7d88772a Iustin Pop
  @type logfile: str
1478 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1479 7d88772a Iustin Pop
  @rtype: int
1480 5fcc718f Iustin Pop
  @return: the value zero
1481 7d88772a Iustin Pop

1482 7d88772a Iustin Pop
  """
1483 7d88772a Iustin Pop
  UMASK = 077
1484 7d88772a Iustin Pop
  WORKDIR = "/"
1485 8f765069 Iustin Pop
1486 8f765069 Iustin Pop
  # this might fail
1487 8f765069 Iustin Pop
  pid = os.fork()
1488 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1489 8f765069 Iustin Pop
    os.setsid()
1490 8f765069 Iustin Pop
    # this might fail
1491 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1492 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1493 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1494 8f765069 Iustin Pop
      os.umask(UMASK)
1495 8f765069 Iustin Pop
    else:
1496 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1497 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1498 8f765069 Iustin Pop
  else:
1499 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1500 8f765069 Iustin Pop
1501 7d88772a Iustin Pop
  for fd in range(3):
1502 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1503 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1504 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1505 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1506 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1507 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1508 7d88772a Iustin Pop
  os.dup2(1, 2)
1509 8f765069 Iustin Pop
  return 0
1510 57c177af Iustin Pop
1511 57c177af Iustin Pop
1512 53beffbb Iustin Pop
def DaemonPidFileName(name):
1513 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1514 58885d79 Iustin Pop

1515 58885d79 Iustin Pop
  @type name: str
1516 58885d79 Iustin Pop
  @param name: the daemon name
1517 58885d79 Iustin Pop
  @rtype: str
1518 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1519 58885d79 Iustin Pop
      daemon name
1520 b330ac0b Guido Trotter

1521 b330ac0b Guido Trotter
  """
1522 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1523 b330ac0b Guido Trotter
1524 b330ac0b Guido Trotter
1525 b330ac0b Guido Trotter
def WritePidFile(name):
1526 b330ac0b Guido Trotter
  """Write the current process pidfile.
1527 b330ac0b Guido Trotter

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

1530 58885d79 Iustin Pop
  @type name: str
1531 58885d79 Iustin Pop
  @param name: the daemon name to use
1532 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1533 58885d79 Iustin Pop
      points to a live process
1534 b330ac0b Guido Trotter

1535 b330ac0b Guido Trotter
  """
1536 b330ac0b Guido Trotter
  pid = os.getpid()
1537 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1538 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1539 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1540 b330ac0b Guido Trotter
1541 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1542 b330ac0b Guido Trotter
1543 b330ac0b Guido Trotter
1544 b330ac0b Guido Trotter
def RemovePidFile(name):
1545 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1546 b330ac0b Guido Trotter

1547 b330ac0b Guido Trotter
  Any errors are ignored.
1548 b330ac0b Guido Trotter

1549 58885d79 Iustin Pop
  @type name: str
1550 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1551 58885d79 Iustin Pop

1552 b330ac0b Guido Trotter
  """
1553 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1554 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1555 b330ac0b Guido Trotter
  try:
1556 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1557 b330ac0b Guido Trotter
  except:
1558 b330ac0b Guido Trotter
    pass
1559 b330ac0b Guido Trotter
1560 b330ac0b Guido Trotter
1561 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1562 ff5251bc Iustin Pop
                waitpid=False):
1563 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1564 b2a1f511 Iustin Pop

1565 b2a1f511 Iustin Pop
  @type pid: int
1566 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1567 38206f3c Iustin Pop
  @type signal_: int
1568 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1569 b2a1f511 Iustin Pop
  @type timeout: int
1570 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1571 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1572 b2a1f511 Iustin Pop
                  will be done
1573 ff5251bc Iustin Pop
  @type waitpid: boolean
1574 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1575 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1576 ff5251bc Iustin Pop
      would remain as zombie
1577 b2a1f511 Iustin Pop

1578 b2a1f511 Iustin Pop
  """
1579 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1580 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1581 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1582 ff5251bc Iustin Pop
    if wait:
1583 ff5251bc Iustin Pop
      try:
1584 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1585 ff5251bc Iustin Pop
      except OSError:
1586 ff5251bc Iustin Pop
        pass
1587 ff5251bc Iustin Pop
1588 b2a1f511 Iustin Pop
  if pid <= 0:
1589 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1590 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1591 b2a1f511 Iustin Pop
1592 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1593 b2a1f511 Iustin Pop
    return
1594 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1595 b2a1f511 Iustin Pop
  if timeout <= 0:
1596 b2a1f511 Iustin Pop
    return
1597 7167159a Michael Hanselmann
1598 7167159a Michael Hanselmann
  # Wait up to $timeout seconds
1599 b2a1f511 Iustin Pop
  end = time.time() + timeout
1600 7167159a Michael Hanselmann
  wait = 0.01
1601 b2a1f511 Iustin Pop
  while time.time() < end and IsProcessAlive(pid):
1602 7167159a Michael Hanselmann
    try:
1603 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1604 7167159a Michael Hanselmann
      if result_pid > 0:
1605 7167159a Michael Hanselmann
        break
1606 7167159a Michael Hanselmann
    except OSError:
1607 7167159a Michael Hanselmann
      pass
1608 7167159a Michael Hanselmann
    time.sleep(wait)
1609 7167159a Michael Hanselmann
    # Make wait time longer for next try
1610 7167159a Michael Hanselmann
    if wait < 0.1:
1611 7167159a Michael Hanselmann
      wait *= 1.5
1612 7167159a Michael Hanselmann
1613 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1614 7167159a Michael Hanselmann
    # Kill process if it's still alive
1615 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1616 b2a1f511 Iustin Pop
1617 b2a1f511 Iustin Pop
1618 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1619 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1620 57c177af Iustin Pop

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

1624 58885d79 Iustin Pop
  @type name: str
1625 58885d79 Iustin Pop
  @param name: the name to look for
1626 58885d79 Iustin Pop
  @type search_path: str
1627 58885d79 Iustin Pop
  @param search_path: location to start at
1628 58885d79 Iustin Pop
  @type test: callable
1629 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1630 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1631 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1632 58885d79 Iustin Pop
  @rtype: str or None
1633 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1634 57c177af Iustin Pop

1635 57c177af Iustin Pop
  """
1636 57c177af Iustin Pop
  for dir_name in search_path:
1637 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1638 57c177af Iustin Pop
    if test(item_name):
1639 57c177af Iustin Pop
      return item_name
1640 57c177af Iustin Pop
  return None
1641 8d1a2a64 Michael Hanselmann
1642 8d1a2a64 Michael Hanselmann
1643 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1644 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1645 8d1a2a64 Michael Hanselmann

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

1649 58885d79 Iustin Pop
  @type vglist: dict
1650 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1651 58885d79 Iustin Pop
  @type vgname: str
1652 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1653 58885d79 Iustin Pop
  @type minsize: int
1654 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1655 58885d79 Iustin Pop
  @rtype: None or str
1656 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1657 8d1a2a64 Michael Hanselmann

1658 8d1a2a64 Michael Hanselmann
  """
1659 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1660 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1661 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1662 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1663 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1664 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1665 8d1a2a64 Michael Hanselmann
  return None
1666 7996a135 Iustin Pop
1667 7996a135 Iustin Pop
1668 45bc5e4a Michael Hanselmann
def SplitTime(value):
1669 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1670 739be818 Michael Hanselmann

1671 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1672 45bc5e4a Michael Hanselmann
  @type value: int or float
1673 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1674 739be818 Michael Hanselmann

1675 739be818 Michael Hanselmann
  """
1676 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1677 45bc5e4a Michael Hanselmann
1678 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1679 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1680 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1681 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1682 45bc5e4a Michael Hanselmann
1683 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1684 739be818 Michael Hanselmann
1685 739be818 Michael Hanselmann
1686 739be818 Michael Hanselmann
def MergeTime(timetuple):
1687 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1688 739be818 Michael Hanselmann

1689 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1690 739be818 Michael Hanselmann
  @type timetuple: tuple
1691 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1692 739be818 Michael Hanselmann

1693 739be818 Michael Hanselmann
  """
1694 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1695 739be818 Michael Hanselmann
1696 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1697 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1698 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1699 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1700 739be818 Michael Hanselmann
1701 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1702 739be818 Michael Hanselmann
1703 739be818 Michael Hanselmann
1704 4a8b186a Michael Hanselmann
def GetNodeDaemonPort():
1705 4a8b186a Michael Hanselmann
  """Get the node daemon port for this cluster.
1706 4a8b186a Michael Hanselmann

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

1711 58885d79 Iustin Pop
  @rtype: int
1712 58885d79 Iustin Pop

1713 4a8b186a Michael Hanselmann
  """
1714 4a8b186a Michael Hanselmann
  try:
1715 4a8b186a Michael Hanselmann
    port = socket.getservbyname("ganeti-noded", "tcp")
1716 4a8b186a Michael Hanselmann
  except socket.error:
1717 4a8b186a Michael Hanselmann
    port = constants.DEFAULT_NODED_PORT
1718 4a8b186a Michael Hanselmann
1719 4a8b186a Michael Hanselmann
  return port
1720 4a8b186a Michael Hanselmann
1721 4a8b186a Michael Hanselmann
1722 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1723 d21d09d6 Iustin Pop
                 multithreaded=False):
1724 82d9caef Iustin Pop
  """Configures the logging module.
1725 82d9caef Iustin Pop

1726 58885d79 Iustin Pop
  @type logfile: str
1727 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1728 58885d79 Iustin Pop
  @type debug: boolean
1729 58885d79 Iustin Pop
  @param debug: whether to enable debug messages too or
1730 58885d79 Iustin Pop
      only those at C{INFO} and above level
1731 58885d79 Iustin Pop
  @type stderr_logging: boolean
1732 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1733 58885d79 Iustin Pop
  @type program: str
1734 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1735 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1736 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1737 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1738 58885d79 Iustin Pop
      stderr logging is disabled
1739 58885d79 Iustin Pop

1740 82d9caef Iustin Pop
  """
1741 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1742 d21d09d6 Iustin Pop
  if multithreaded:
1743 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1744 82d9caef Iustin Pop
  if debug:
1745 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1746 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1747 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1748 82d9caef Iustin Pop
1749 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1750 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1751 82d9caef Iustin Pop
1752 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1753 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1754 7d88772a Iustin Pop
    handler.close()
1755 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1756 6346a9e5 Michael Hanselmann
1757 82d9caef Iustin Pop
  if stderr_logging:
1758 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1759 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1760 82d9caef Iustin Pop
    if debug:
1761 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1762 82d9caef Iustin Pop
    else:
1763 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1764 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1765 82d9caef Iustin Pop
1766 82d9caef Iustin Pop
  # this can fail, if the logging directories are not setup or we have
1767 82d9caef Iustin Pop
  # a permisssion problem; in this case, it's best to log but ignore
1768 82d9caef Iustin Pop
  # the error if stderr_logging is True, and if false we re-raise the
1769 82d9caef Iustin Pop
  # exception since otherwise we could run but without any logs at all
1770 82d9caef Iustin Pop
  try:
1771 82d9caef Iustin Pop
    logfile_handler = logging.FileHandler(logfile)
1772 82d9caef Iustin Pop
    logfile_handler.setFormatter(formatter)
1773 82d9caef Iustin Pop
    if debug:
1774 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.DEBUG)
1775 82d9caef Iustin Pop
    else:
1776 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.INFO)
1777 82d9caef Iustin Pop
    root_logger.addHandler(logfile_handler)
1778 d21d09d6 Iustin Pop
  except EnvironmentError:
1779 82d9caef Iustin Pop
    if stderr_logging:
1780 82d9caef Iustin Pop
      logging.exception("Failed to enable logging to file '%s'", logfile)
1781 82d9caef Iustin Pop
    else:
1782 82d9caef Iustin Pop
      # we need to re-raise the exception
1783 82d9caef Iustin Pop
      raise
1784 82d9caef Iustin Pop
1785 82d9caef Iustin Pop
1786 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1787 f65f63ef Iustin Pop
  """Return the last lines from a file.
1788 f65f63ef Iustin Pop

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

1793 f65f63ef Iustin Pop
  @param fname: the file name
1794 f65f63ef Iustin Pop
  @type lines: int
1795 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1796 f65f63ef Iustin Pop

1797 f65f63ef Iustin Pop
  """
1798 f65f63ef Iustin Pop
  fd = open(fname, "r")
1799 f65f63ef Iustin Pop
  try:
1800 f65f63ef Iustin Pop
    fd.seek(0, 2)
1801 f65f63ef Iustin Pop
    pos = fd.tell()
1802 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1803 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1804 f65f63ef Iustin Pop
    raw_data = fd.read()
1805 f65f63ef Iustin Pop
  finally:
1806 f65f63ef Iustin Pop
    fd.close()
1807 f65f63ef Iustin Pop
1808 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1809 f65f63ef Iustin Pop
  return rows[-lines:]
1810 f65f63ef Iustin Pop
1811 f65f63ef Iustin Pop
1812 26f15862 Iustin Pop
def SafeEncode(text):
1813 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1814 26f15862 Iustin Pop

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

1824 26f15862 Iustin Pop
  @type text: str or unicode
1825 26f15862 Iustin Pop
  @param text: input data
1826 26f15862 Iustin Pop
  @rtype: str
1827 26f15862 Iustin Pop
  @return: a safe version of text
1828 26f15862 Iustin Pop

1829 26f15862 Iustin Pop
  """
1830 d392fa34 Iustin Pop
  if isinstance(text, unicode):
1831 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
1832 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
1833 d392fa34 Iustin Pop
  resu = ""
1834 d392fa34 Iustin Pop
  for char in text:
1835 d392fa34 Iustin Pop
    c = ord(char)
1836 d392fa34 Iustin Pop
    if char  == '\t':
1837 d392fa34 Iustin Pop
      resu += r'\t'
1838 d392fa34 Iustin Pop
    elif char == '\n':
1839 d392fa34 Iustin Pop
      resu += r'\n'
1840 d392fa34 Iustin Pop
    elif char == '\r':
1841 d392fa34 Iustin Pop
      resu += r'\'r'
1842 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
1843 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
1844 d392fa34 Iustin Pop
    else:
1845 d392fa34 Iustin Pop
      resu += char
1846 d392fa34 Iustin Pop
  return resu
1847 26f15862 Iustin Pop
1848 26f15862 Iustin Pop
1849 835528af Iustin Pop
def CommaJoin(names):
1850 835528af Iustin Pop
  """Nicely join a set of identifiers.
1851 835528af Iustin Pop

1852 835528af Iustin Pop
  @param names: set, list or tuple
1853 835528af Iustin Pop
  @return: a string with the formatted results
1854 835528af Iustin Pop

1855 835528af Iustin Pop
  """
1856 835528af Iustin Pop
  return ", ".join(["'%s'" % val for val in names])
1857 835528af Iustin Pop
1858 835528af Iustin Pop
1859 7996a135 Iustin Pop
def LockedMethod(fn):
1860 7996a135 Iustin Pop
  """Synchronized object access decorator.
1861 7996a135 Iustin Pop

1862 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1863 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1864 7996a135 Iustin Pop

1865 7996a135 Iustin Pop
  """
1866 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1867 e67bd559 Michael Hanselmann
    if debug_locks:
1868 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1869 e67bd559 Michael Hanselmann
1870 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1871 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1872 7996a135 Iustin Pop
    lock = self._lock
1873 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1874 7996a135 Iustin Pop
    lock.acquire()
1875 7996a135 Iustin Pop
    try:
1876 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1877 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1878 7996a135 Iustin Pop
    finally:
1879 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1880 7996a135 Iustin Pop
      lock.release()
1881 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1882 7996a135 Iustin Pop
    return result
1883 7996a135 Iustin Pop
  return wrapper
1884 eb0f0ce0 Michael Hanselmann
1885 eb0f0ce0 Michael Hanselmann
1886 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1887 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1888 eb0f0ce0 Michael Hanselmann

1889 58885d79 Iustin Pop
  @type fd: int
1890 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1891 58885d79 Iustin Pop

1892 eb0f0ce0 Michael Hanselmann
  """
1893 eb0f0ce0 Michael Hanselmann
  try:
1894 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1895 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1896 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1897 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1898 eb0f0ce0 Michael Hanselmann
    raise
1899 de499029 Michael Hanselmann
1900 de499029 Michael Hanselmann
1901 a87b4824 Michael Hanselmann
class FileLock(object):
1902 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1903 a87b4824 Michael Hanselmann

1904 a87b4824 Michael Hanselmann
  """
1905 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1906 58885d79 Iustin Pop
    """Constructor for FileLock.
1907 58885d79 Iustin Pop

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

1910 58885d79 Iustin Pop
    @type filename: str
1911 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1912 58885d79 Iustin Pop

1913 58885d79 Iustin Pop
    """
1914 a87b4824 Michael Hanselmann
    self.filename = filename
1915 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1916 a87b4824 Michael Hanselmann
1917 a87b4824 Michael Hanselmann
  def __del__(self):
1918 a87b4824 Michael Hanselmann
    self.Close()
1919 a87b4824 Michael Hanselmann
1920 a87b4824 Michael Hanselmann
  def Close(self):
1921 58885d79 Iustin Pop
    """Close the file and release the lock.
1922 58885d79 Iustin Pop

1923 58885d79 Iustin Pop
    """
1924 a87b4824 Michael Hanselmann
    if self.fd:
1925 a87b4824 Michael Hanselmann
      self.fd.close()
1926 a87b4824 Michael Hanselmann
      self.fd = None
1927 a87b4824 Michael Hanselmann
1928 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
1929 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
1930 aa74b828 Michael Hanselmann

1931 aa74b828 Michael Hanselmann
    @type flag: int
1932 58885d79 Iustin Pop
    @param flag: operation flag
1933 aa74b828 Michael Hanselmann
    @type blocking: bool
1934 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
1935 aa74b828 Michael Hanselmann
    @type timeout: None or float
1936 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
1937 aa74b828 Michael Hanselmann
                    non-blocking mode).
1938 aa74b828 Michael Hanselmann
    @type errmsg: string
1939 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
1940 aa74b828 Michael Hanselmann

1941 aa74b828 Michael Hanselmann
    """
1942 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1943 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
1944 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
1945 a87b4824 Michael Hanselmann
1946 aa74b828 Michael Hanselmann
    if timeout is not None:
1947 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1948 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
1949 a87b4824 Michael Hanselmann
1950 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
1951 aa74b828 Michael Hanselmann
    elif not blocking:
1952 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1953 aa74b828 Michael Hanselmann
      timeout_end = None
1954 aa74b828 Michael Hanselmann
1955 aa74b828 Michael Hanselmann
    retry = True
1956 aa74b828 Michael Hanselmann
    while retry:
1957 aa74b828 Michael Hanselmann
      try:
1958 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
1959 aa74b828 Michael Hanselmann
        retry = False
1960 aa74b828 Michael Hanselmann
      except IOError, err:
1961 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
1962 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
1963 aa74b828 Michael Hanselmann
            # Wait before trying again
1964 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
1965 aa74b828 Michael Hanselmann
          else:
1966 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
1967 aa74b828 Michael Hanselmann
        else:
1968 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
1969 aa74b828 Michael Hanselmann
          raise
1970 aa74b828 Michael Hanselmann
1971 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
1972 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1973 a87b4824 Michael Hanselmann

1974 58885d79 Iustin Pop
    @type blocking: boolean
1975 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1976 58885d79 Iustin Pop
        can lock the file or return immediately
1977 58885d79 Iustin Pop
    @type timeout: int or None
1978 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1979 58885d79 Iustin Pop
        (in blocking mode)
1980 58885d79 Iustin Pop

1981 a87b4824 Michael Hanselmann
    """
1982 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
1983 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1984 a87b4824 Michael Hanselmann
1985 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
1986 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1987 a87b4824 Michael Hanselmann

1988 58885d79 Iustin Pop
    @type blocking: boolean
1989 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1990 58885d79 Iustin Pop
        can lock the file or return immediately
1991 58885d79 Iustin Pop
    @type timeout: int or None
1992 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1993 58885d79 Iustin Pop
        (in blocking mode)
1994 58885d79 Iustin Pop

1995 a87b4824 Michael Hanselmann
    """
1996 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
1997 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1998 a87b4824 Michael Hanselmann
1999 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2000 a87b4824 Michael Hanselmann
    """Unlocks the file.
2001 a87b4824 Michael Hanselmann

2002 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2003 58885d79 Iustin Pop
    operation::
2004 58885d79 Iustin Pop

2005 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2006 58885d79 Iustin Pop
      operations.
2007 58885d79 Iustin Pop

2008 58885d79 Iustin Pop
    @type blocking: boolean
2009 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2010 58885d79 Iustin Pop
        can lock the file or return immediately
2011 58885d79 Iustin Pop
    @type timeout: int or None
2012 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2013 58885d79 Iustin Pop
        (in blocking mode)
2014 a87b4824 Michael Hanselmann

2015 a87b4824 Michael Hanselmann
    """
2016 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2017 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2018 a87b4824 Michael Hanselmann
2019 a87b4824 Michael Hanselmann
2020 de499029 Michael Hanselmann
class SignalHandler(object):
2021 de499029 Michael Hanselmann
  """Generic signal handler class.
2022 de499029 Michael Hanselmann

2023 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2024 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2025 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2026 58885d79 Iustin Pop
  signal was sent.
2027 58885d79 Iustin Pop

2028 58885d79 Iustin Pop
  @type signum: list
2029 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2030 58885d79 Iustin Pop
  @type called: boolean
2031 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2032 de499029 Michael Hanselmann

2033 de499029 Michael Hanselmann
  """
2034 de499029 Michael Hanselmann
  def __init__(self, signum):
2035 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2036 de499029 Michael Hanselmann

2037 58885d79 Iustin Pop
    @type signum: int or list of ints
2038 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2039 de499029 Michael Hanselmann

2040 de499029 Michael Hanselmann
    """
2041 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
2042 de499029 Michael Hanselmann
      self.signum = set([signum])
2043 de499029 Michael Hanselmann
    else:
2044 de499029 Michael Hanselmann
      self.signum = set(signum)
2045 de499029 Michael Hanselmann
2046 de499029 Michael Hanselmann
    self.called = False
2047 de499029 Michael Hanselmann
2048 de499029 Michael Hanselmann
    self._previous = {}
2049 de499029 Michael Hanselmann
    try:
2050 de499029 Michael Hanselmann
      for signum in self.signum:
2051 de499029 Michael Hanselmann
        # Setup handler
2052 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2053 de499029 Michael Hanselmann
        try:
2054 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2055 de499029 Michael Hanselmann
        except:
2056 de499029 Michael Hanselmann
          # Restore previous handler
2057 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2058 de499029 Michael Hanselmann
          raise
2059 de499029 Michael Hanselmann
    except:
2060 de499029 Michael Hanselmann
      # Reset all handlers
2061 de499029 Michael Hanselmann
      self.Reset()
2062 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2063 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2064 de499029 Michael Hanselmann
      raise
2065 de499029 Michael Hanselmann
2066 de499029 Michael Hanselmann
  def __del__(self):
2067 de499029 Michael Hanselmann
    self.Reset()
2068 de499029 Michael Hanselmann
2069 de499029 Michael Hanselmann
  def Reset(self):
2070 de499029 Michael Hanselmann
    """Restore previous handler.
2071 de499029 Michael Hanselmann

2072 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2073 58885d79 Iustin Pop

2074 de499029 Michael Hanselmann
    """
2075 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2076 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2077 de499029 Michael Hanselmann
      # If successful, remove from dict
2078 de499029 Michael Hanselmann
      del self._previous[signum]
2079 de499029 Michael Hanselmann
2080 de499029 Michael Hanselmann
  def Clear(self):
2081 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2082 de499029 Michael Hanselmann

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

2085 de499029 Michael Hanselmann
    """
2086 de499029 Michael Hanselmann
    self.called = False
2087 de499029 Michael Hanselmann
2088 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
2089 de499029 Michael Hanselmann
    """Actual signal handling function.
2090 de499029 Michael Hanselmann

2091 de499029 Michael Hanselmann
    """
2092 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2093 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2094 de499029 Michael Hanselmann
    self.called = True
2095 a2d2e1a7 Iustin Pop
2096 a2d2e1a7 Iustin Pop
2097 a2d2e1a7 Iustin Pop
class FieldSet(object):
2098 a2d2e1a7 Iustin Pop
  """A simple field set.
2099 a2d2e1a7 Iustin Pop

2100 a2d2e1a7 Iustin Pop
  Among the features are:
2101 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2102 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2103 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2104 a2d2e1a7 Iustin Pop

2105 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2106 a2d2e1a7 Iustin Pop

2107 a2d2e1a7 Iustin Pop
  """
2108 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2109 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2110 a2d2e1a7 Iustin Pop
2111 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2112 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2113 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2114 a2d2e1a7 Iustin Pop
2115 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2116 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2117 a2d2e1a7 Iustin Pop

2118 a2d2e1a7 Iustin Pop
    @type field: str
2119 a2d2e1a7 Iustin Pop
    @param field: the string to match
2120 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2121 a2d2e1a7 Iustin Pop

2122 a2d2e1a7 Iustin Pop
    """
2123 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2124 a2d2e1a7 Iustin Pop
      return m
2125 a2d2e1a7 Iustin Pop
    return False
2126 a2d2e1a7 Iustin Pop
2127 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2128 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2129 a2d2e1a7 Iustin Pop

2130 a2d2e1a7 Iustin Pop
    @type items: list
2131 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2132 a2d2e1a7 Iustin Pop
    @rtype: list
2133 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2134 a2d2e1a7 Iustin Pop

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