Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 8e70b181

History | View | Annotate | Download (55.2 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

365 a8083063 Iustin Pop
  """
366 a8083063 Iustin Pop
  ret = {}
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
  for filename in files:
369 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
370 a8083063 Iustin Pop
    if cksum:
371 a8083063 Iustin Pop
      ret[filename] = cksum
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
  return ret
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
376 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
377 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
378 a5728081 Guido Trotter

379 a5728081 Guido Trotter
  @type target: dict
380 a5728081 Guido Trotter
  @param target: the dict to update
381 a5728081 Guido Trotter
  @type key_types: dict
382 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
383 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
384 a5728081 Guido Trotter
  @type allowed_values: list
385 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
386 a5728081 Guido Trotter

387 a5728081 Guido Trotter
  """
388 a5728081 Guido Trotter
  if allowed_values is None:
389 a5728081 Guido Trotter
    allowed_values = []
390 a5728081 Guido Trotter
391 a5728081 Guido Trotter
  for key in target:
392 a5728081 Guido Trotter
    if key not in key_types:
393 a5728081 Guido Trotter
      msg = "Unknown key '%s'" % key
394 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
395 a5728081 Guido Trotter
396 a5728081 Guido Trotter
    if target[key] in allowed_values:
397 a5728081 Guido Trotter
      continue
398 a5728081 Guido Trotter
399 a5728081 Guido Trotter
    type = key_types[key]
400 a5728081 Guido Trotter
    if type not in constants.ENFORCEABLE_TYPES:
401 a5728081 Guido Trotter
      msg = "'%s' has non-enforceable type %s" % (key, type)
402 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
403 a5728081 Guido Trotter
404 a5728081 Guido Trotter
    if type == constants.VTYPE_STRING:
405 a5728081 Guido Trotter
      if not isinstance(target[key], basestring):
406 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
407 a5728081 Guido Trotter
          target[key] = ''
408 a5728081 Guido Trotter
        else:
409 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
410 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
411 a5728081 Guido Trotter
    elif type == constants.VTYPE_BOOL:
412 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
413 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
414 a5728081 Guido Trotter
          target[key] = False
415 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
416 a5728081 Guido Trotter
          target[key] = True
417 a5728081 Guido Trotter
        else:
418 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
419 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
420 a5728081 Guido Trotter
      elif target[key]:
421 a5728081 Guido Trotter
        target[key] = True
422 a5728081 Guido Trotter
      else:
423 a5728081 Guido Trotter
        target[key] = False
424 a5728081 Guido Trotter
    elif type == constants.VTYPE_SIZE:
425 a5728081 Guido Trotter
      try:
426 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
427 a5728081 Guido Trotter
      except errors.UnitParseError, err:
428 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
429 a5728081 Guido Trotter
              (key, target[key], err)
430 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
431 a5728081 Guido Trotter
    elif type == constants.VTYPE_INT:
432 a5728081 Guido Trotter
      try:
433 a5728081 Guido Trotter
        target[key] = int(target[key])
434 a5728081 Guido Trotter
      except (ValueError, TypeError):
435 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
436 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
437 a5728081 Guido Trotter
438 a5728081 Guido Trotter
439 a8083063 Iustin Pop
def IsProcessAlive(pid):
440 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
441 a8083063 Iustin Pop

442 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
443 44bf25ff Iustin Pop
      will be returned as alive
444 58885d79 Iustin Pop
  @type pid: int
445 58885d79 Iustin Pop
  @param pid: the process ID to check
446 58885d79 Iustin Pop
  @rtype: boolean
447 58885d79 Iustin Pop
  @return: True if the process exists
448 a8083063 Iustin Pop

449 a8083063 Iustin Pop
  """
450 d9f311d7 Iustin Pop
  if pid <= 0:
451 d9f311d7 Iustin Pop
    return False
452 d9f311d7 Iustin Pop
453 a8083063 Iustin Pop
  try:
454 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
455 44bf25ff Iustin Pop
    return True
456 44bf25ff Iustin Pop
  except EnvironmentError, err:
457 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
458 a8083063 Iustin Pop
      return False
459 44bf25ff Iustin Pop
    raise
460 a8083063 Iustin Pop
461 a8083063 Iustin Pop
462 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
463 58885d79 Iustin Pop
  """Read a pid from a file.
464 fee80e90 Guido Trotter

465 58885d79 Iustin Pop
  @type  pidfile: string
466 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
467 58885d79 Iustin Pop
  @rtype: int
468 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
469 d9f311d7 Iustin Pop
           otherwise 0
470 fee80e90 Guido Trotter

471 fee80e90 Guido Trotter
  """
472 fee80e90 Guido Trotter
  try:
473 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
474 d9f311d7 Iustin Pop
  except EnvironmentError, err:
475 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
476 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
477 d9f311d7 Iustin Pop
    return 0
478 fee80e90 Guido Trotter
479 fee80e90 Guido Trotter
  try:
480 fee80e90 Guido Trotter
    pid = int(pf.read())
481 d9f311d7 Iustin Pop
  except ValueError, err:
482 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
483 d9f311d7 Iustin Pop
    return 0
484 fee80e90 Guido Trotter
485 d9f311d7 Iustin Pop
  return pid
486 fee80e90 Guido Trotter
487 fee80e90 Guido Trotter
488 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
489 a8083063 Iustin Pop
  """Try to match a name against a list.
490 a8083063 Iustin Pop

491 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
492 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
493 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
494 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
495 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
496 58885d79 Iustin Pop
  'test1.example.org']}).
497 a8083063 Iustin Pop

498 58885d79 Iustin Pop
  @type key: str
499 58885d79 Iustin Pop
  @param key: the name to be searched
500 58885d79 Iustin Pop
  @type name_list: list
501 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
502 a8083063 Iustin Pop

503 58885d79 Iustin Pop
  @rtype: None or str
504 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
505 58885d79 Iustin Pop
      otherwise the element from the list which matches
506 a8083063 Iustin Pop

507 a8083063 Iustin Pop
  """
508 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
509 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
510 a8083063 Iustin Pop
  if len(names_filtered) != 1:
511 a8083063 Iustin Pop
    return None
512 a8083063 Iustin Pop
  return names_filtered[0]
513 a8083063 Iustin Pop
514 a8083063 Iustin Pop
515 bcf043c9 Iustin Pop
class HostInfo:
516 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
517 bcf043c9 Iustin Pop

518 bcf043c9 Iustin Pop
  """
519 89e1fc26 Iustin Pop
  def __init__(self, name=None):
520 bcf043c9 Iustin Pop
    """Initialize the host name object.
521 bcf043c9 Iustin Pop

522 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
523 89e1fc26 Iustin Pop
    name.
524 bcf043c9 Iustin Pop

525 bcf043c9 Iustin Pop
    """
526 89e1fc26 Iustin Pop
    if name is None:
527 89e1fc26 Iustin Pop
      name = self.SysName()
528 89e1fc26 Iustin Pop
529 89e1fc26 Iustin Pop
    self.query = name
530 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
531 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
532 bcf043c9 Iustin Pop
533 c8a0948f Michael Hanselmann
  def ShortName(self):
534 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
535 c8a0948f Michael Hanselmann

536 c8a0948f Michael Hanselmann
    """
537 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
538 c8a0948f Michael Hanselmann
539 89e1fc26 Iustin Pop
  @staticmethod
540 89e1fc26 Iustin Pop
  def SysName():
541 89e1fc26 Iustin Pop
    """Return the current system's name.
542 bcf043c9 Iustin Pop

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

545 89e1fc26 Iustin Pop
    """
546 89e1fc26 Iustin Pop
    return socket.gethostname()
547 a8083063 Iustin Pop
548 89e1fc26 Iustin Pop
  @staticmethod
549 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
550 89e1fc26 Iustin Pop
    """Look up hostname
551 a8083063 Iustin Pop

552 58885d79 Iustin Pop
    @type hostname: str
553 58885d79 Iustin Pop
    @param hostname: hostname to look up
554 89e1fc26 Iustin Pop

555 58885d79 Iustin Pop
    @rtype: tuple
556 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
557 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
558 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
559 89e1fc26 Iustin Pop

560 89e1fc26 Iustin Pop
    """
561 89e1fc26 Iustin Pop
    try:
562 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
563 89e1fc26 Iustin Pop
    except socket.gaierror, err:
564 89e1fc26 Iustin Pop
      # hostname not found in DNS
565 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
566 a8083063 Iustin Pop
567 89e1fc26 Iustin Pop
    return result
568 a8083063 Iustin Pop
569 a8083063 Iustin Pop
570 a8083063 Iustin Pop
def ListVolumeGroups():
571 a8083063 Iustin Pop
  """List volume groups and their size
572 a8083063 Iustin Pop

573 58885d79 Iustin Pop
  @rtype: dict
574 58885d79 Iustin Pop
  @return:
575 58885d79 Iustin Pop
       Dictionary with keys volume name and values
576 58885d79 Iustin Pop
       the size of the volume
577 a8083063 Iustin Pop

578 a8083063 Iustin Pop
  """
579 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
580 a8083063 Iustin Pop
  result = RunCmd(command)
581 a8083063 Iustin Pop
  retval = {}
582 a8083063 Iustin Pop
  if result.failed:
583 a8083063 Iustin Pop
    return retval
584 a8083063 Iustin Pop
585 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
586 a8083063 Iustin Pop
    try:
587 a8083063 Iustin Pop
      name, size = line.split()
588 a8083063 Iustin Pop
      size = int(float(size))
589 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
590 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
591 a8083063 Iustin Pop
      continue
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
    retval[name] = size
594 a8083063 Iustin Pop
595 a8083063 Iustin Pop
  return retval
596 a8083063 Iustin Pop
597 a8083063 Iustin Pop
598 a8083063 Iustin Pop
def BridgeExists(bridge):
599 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
600 a8083063 Iustin Pop

601 58885d79 Iustin Pop
  @type bridge: str
602 58885d79 Iustin Pop
  @param bridge: the bridge name to check
603 58885d79 Iustin Pop
  @rtype: boolean
604 58885d79 Iustin Pop
  @return: True if it does
605 a8083063 Iustin Pop

606 a8083063 Iustin Pop
  """
607 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
608 a8083063 Iustin Pop
609 a8083063 Iustin Pop
610 a8083063 Iustin Pop
def NiceSort(name_list):
611 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
612 a8083063 Iustin Pop

613 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
614 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
615 58885d79 Iustin Pop
  'a11']}.
616 a8083063 Iustin Pop

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

621 58885d79 Iustin Pop
  @type name_list: list
622 58885d79 Iustin Pop
  @param name_list: the names to be sorted
623 58885d79 Iustin Pop
  @rtype: list
624 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
625 a8083063 Iustin Pop

626 a8083063 Iustin Pop
  """
627 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
628 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
629 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
630 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
631 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
632 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
633 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
634 a8083063 Iustin Pop
  def _TryInt(val):
635 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
636 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
637 a8083063 Iustin Pop
      return val
638 a8083063 Iustin Pop
    rval = int(val)
639 a8083063 Iustin Pop
    return rval
640 a8083063 Iustin Pop
641 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
642 a8083063 Iustin Pop
             for name in name_list]
643 a8083063 Iustin Pop
  to_sort.sort()
644 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
645 a8083063 Iustin Pop
646 a8083063 Iustin Pop
647 a8083063 Iustin Pop
def TryConvert(fn, val):
648 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
649 a8083063 Iustin Pop

650 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
651 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
652 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
653 58885d79 Iustin Pop
  exceptions are propagated to the caller.
654 58885d79 Iustin Pop

655 58885d79 Iustin Pop
  @type fn: callable
656 58885d79 Iustin Pop
  @param fn: function to apply to the value
657 58885d79 Iustin Pop
  @param val: the value to be converted
658 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
659 58885d79 Iustin Pop
      otherwise the original value.
660 a8083063 Iustin Pop

661 a8083063 Iustin Pop
  """
662 a8083063 Iustin Pop
  try:
663 a8083063 Iustin Pop
    nv = fn(val)
664 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
665 a8083063 Iustin Pop
    nv = val
666 a8083063 Iustin Pop
  return nv
667 a8083063 Iustin Pop
668 a8083063 Iustin Pop
669 a8083063 Iustin Pop
def IsValidIP(ip):
670 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
671 a8083063 Iustin Pop

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

675 58885d79 Iustin Pop
  @type ip: str
676 58885d79 Iustin Pop
  @param ip: the address to be checked
677 58885d79 Iustin Pop
  @rtype: a regular expression match object
678 58885d79 Iustin Pop
  @return: a regular epression match object, or None if the
679 58885d79 Iustin Pop
      address is not valid
680 a8083063 Iustin Pop

681 a8083063 Iustin Pop
  """
682 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
683 58885d79 Iustin Pop
  #TODO: convert and return only boolean
684 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
685 a8083063 Iustin Pop
686 a8083063 Iustin Pop
687 a8083063 Iustin Pop
def IsValidShellParam(word):
688 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
689 a8083063 Iustin Pop

690 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
691 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
692 a8083063 Iustin Pop
  the actual command.
693 a8083063 Iustin Pop

694 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
695 a8083063 Iustin Pop
  side.
696 a8083063 Iustin Pop

697 58885d79 Iustin Pop
  @type word: str
698 58885d79 Iustin Pop
  @param word: the word to check
699 58885d79 Iustin Pop
  @rtype: boolean
700 58885d79 Iustin Pop
  @return: True if the word is 'safe'
701 58885d79 Iustin Pop

702 a8083063 Iustin Pop
  """
703 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
704 a8083063 Iustin Pop
705 a8083063 Iustin Pop
706 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
707 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
708 a8083063 Iustin Pop

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

714 58885d79 Iustin Pop
  @type template: str
715 58885d79 Iustin Pop
  @param template: the string holding the template for the
716 58885d79 Iustin Pop
      string formatting
717 58885d79 Iustin Pop
  @rtype: str
718 58885d79 Iustin Pop
  @return: the expanded command line
719 58885d79 Iustin Pop

720 a8083063 Iustin Pop
  """
721 a8083063 Iustin Pop
  for word in args:
722 a8083063 Iustin Pop
    if not IsValidShellParam(word):
723 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
724 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
725 a8083063 Iustin Pop
  return template % args
726 a8083063 Iustin Pop
727 a8083063 Iustin Pop
728 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
729 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
730 a8083063 Iustin Pop

731 58885d79 Iustin Pop
  @type value: int
732 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
733 9fbfbb7b Iustin Pop
  @type units: char
734 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
735 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
736 9fbfbb7b Iustin Pop
      - 'm' for MiBs
737 9fbfbb7b Iustin Pop
      - 'g' for GiBs
738 9fbfbb7b Iustin Pop
      - 't' for TiBs
739 58885d79 Iustin Pop
  @rtype: str
740 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
741 a8083063 Iustin Pop

742 a8083063 Iustin Pop
  """
743 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
744 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
745 a8083063 Iustin Pop
746 9fbfbb7b Iustin Pop
  suffix = ''
747 9fbfbb7b Iustin Pop
748 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
749 9fbfbb7b Iustin Pop
    if units == 'h':
750 9fbfbb7b Iustin Pop
      suffix = 'M'
751 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
752 9fbfbb7b Iustin Pop
753 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
754 9fbfbb7b Iustin Pop
    if units == 'h':
755 9fbfbb7b Iustin Pop
      suffix = 'G'
756 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
757 a8083063 Iustin Pop
758 a8083063 Iustin Pop
  else:
759 9fbfbb7b Iustin Pop
    if units == 'h':
760 9fbfbb7b Iustin Pop
      suffix = 'T'
761 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
762 a8083063 Iustin Pop
763 a8083063 Iustin Pop
764 a8083063 Iustin Pop
def ParseUnit(input_string):
765 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
766 a8083063 Iustin Pop

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

771 a8083063 Iustin Pop
  """
772 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
773 a8083063 Iustin Pop
  if not m:
774 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
775 a8083063 Iustin Pop
776 a8083063 Iustin Pop
  value = float(m.groups()[0])
777 a8083063 Iustin Pop
778 a8083063 Iustin Pop
  unit = m.groups()[1]
779 a8083063 Iustin Pop
  if unit:
780 a8083063 Iustin Pop
    lcunit = unit.lower()
781 a8083063 Iustin Pop
  else:
782 a8083063 Iustin Pop
    lcunit = 'm'
783 a8083063 Iustin Pop
784 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
785 a8083063 Iustin Pop
    # Value already in MiB
786 a8083063 Iustin Pop
    pass
787 a8083063 Iustin Pop
788 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
789 a8083063 Iustin Pop
    value *= 1024
790 a8083063 Iustin Pop
791 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
792 a8083063 Iustin Pop
    value *= 1024 * 1024
793 a8083063 Iustin Pop
794 a8083063 Iustin Pop
  else:
795 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
796 a8083063 Iustin Pop
797 a8083063 Iustin Pop
  # Make sure we round up
798 a8083063 Iustin Pop
  if int(value) < value:
799 a8083063 Iustin Pop
    value += 1
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
  # Round up to the next multiple of 4
802 a8083063 Iustin Pop
  value = int(value)
803 a8083063 Iustin Pop
  if value % 4:
804 a8083063 Iustin Pop
    value += 4 - value % 4
805 a8083063 Iustin Pop
806 a8083063 Iustin Pop
  return value
807 a8083063 Iustin Pop
808 a8083063 Iustin Pop
809 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
810 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
811 a8083063 Iustin Pop

812 58885d79 Iustin Pop
  @type file_name: str
813 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
814 58885d79 Iustin Pop
  @type key: str
815 58885d79 Iustin Pop
  @param key: string containing key
816 58885d79 Iustin Pop

817 a8083063 Iustin Pop
  """
818 a8083063 Iustin Pop
  key_fields = key.split()
819 a8083063 Iustin Pop
820 a8083063 Iustin Pop
  f = open(file_name, 'a+')
821 a8083063 Iustin Pop
  try:
822 a8083063 Iustin Pop
    nl = True
823 a8083063 Iustin Pop
    for line in f:
824 a8083063 Iustin Pop
      # Ignore whitespace changes
825 a8083063 Iustin Pop
      if line.split() == key_fields:
826 a8083063 Iustin Pop
        break
827 a8083063 Iustin Pop
      nl = line.endswith('\n')
828 a8083063 Iustin Pop
    else:
829 a8083063 Iustin Pop
      if not nl:
830 a8083063 Iustin Pop
        f.write("\n")
831 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
832 a8083063 Iustin Pop
      f.write("\n")
833 a8083063 Iustin Pop
      f.flush()
834 a8083063 Iustin Pop
  finally:
835 a8083063 Iustin Pop
    f.close()
836 a8083063 Iustin Pop
837 a8083063 Iustin Pop
838 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
839 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
840 a8083063 Iustin Pop

841 58885d79 Iustin Pop
  @type file_name: str
842 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
843 58885d79 Iustin Pop
  @type key: str
844 58885d79 Iustin Pop
  @param key: string containing key
845 58885d79 Iustin Pop

846 a8083063 Iustin Pop
  """
847 a8083063 Iustin Pop
  key_fields = key.split()
848 a8083063 Iustin Pop
849 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
850 a8083063 Iustin Pop
  try:
851 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
852 a8083063 Iustin Pop
    try:
853 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
854 59f82e3f Michael Hanselmann
      try:
855 59f82e3f Michael Hanselmann
        for line in f:
856 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
857 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
858 59f82e3f Michael Hanselmann
            out.write(line)
859 899d2a81 Michael Hanselmann
860 899d2a81 Michael Hanselmann
        out.flush()
861 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
862 899d2a81 Michael Hanselmann
      finally:
863 899d2a81 Michael Hanselmann
        f.close()
864 899d2a81 Michael Hanselmann
    finally:
865 899d2a81 Michael Hanselmann
      out.close()
866 899d2a81 Michael Hanselmann
  except:
867 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
868 899d2a81 Michael Hanselmann
    raise
869 899d2a81 Michael Hanselmann
870 899d2a81 Michael Hanselmann
871 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
872 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
873 899d2a81 Michael Hanselmann

874 58885d79 Iustin Pop
  @type file_name: str
875 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
876 58885d79 Iustin Pop
  @type ip: str
877 58885d79 Iustin Pop
  @param ip: the IP address
878 58885d79 Iustin Pop
  @type hostname: str
879 58885d79 Iustin Pop
  @param hostname: the hostname to be added
880 58885d79 Iustin Pop
  @type aliases: list
881 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
882 58885d79 Iustin Pop

883 899d2a81 Michael Hanselmann
  """
884 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
885 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
886 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
887 7fbb1f65 Michael Hanselmann
888 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
889 899d2a81 Michael Hanselmann
  try:
890 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
891 9440aeab Michael Hanselmann
    try:
892 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
893 9440aeab Michael Hanselmann
      try:
894 9440aeab Michael Hanselmann
        for line in f:
895 9440aeab Michael Hanselmann
          fields = line.split()
896 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
897 9440aeab Michael Hanselmann
            continue
898 9440aeab Michael Hanselmann
          out.write(line)
899 9440aeab Michael Hanselmann
900 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
901 9440aeab Michael Hanselmann
        if aliases:
902 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
903 9440aeab Michael Hanselmann
        out.write('\n')
904 9440aeab Michael Hanselmann
905 9440aeab Michael Hanselmann
        out.flush()
906 2e3e75b7 Michael Hanselmann
        os.fsync(out)
907 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
908 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
909 9440aeab Michael Hanselmann
      finally:
910 9440aeab Michael Hanselmann
        f.close()
911 9440aeab Michael Hanselmann
    finally:
912 9440aeab Michael Hanselmann
      out.close()
913 9440aeab Michael Hanselmann
  except:
914 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
915 9440aeab Michael Hanselmann
    raise
916 899d2a81 Michael Hanselmann
917 899d2a81 Michael Hanselmann
918 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
919 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
920 d9c02ca6 Michael Hanselmann

921 58885d79 Iustin Pop
  @type hostname: str
922 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
923 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
924 58885d79 Iustin Pop

925 d9c02ca6 Michael Hanselmann
  """
926 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
927 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
928 d9c02ca6 Michael Hanselmann
929 d9c02ca6 Michael Hanselmann
930 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
931 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
932 899d2a81 Michael Hanselmann

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

935 58885d79 Iustin Pop
  @type file_name: str
936 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
937 58885d79 Iustin Pop
  @type hostname: str
938 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
939 58885d79 Iustin Pop

940 899d2a81 Michael Hanselmann
  """
941 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
942 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
943 899d2a81 Michael Hanselmann
  try:
944 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
945 899d2a81 Michael Hanselmann
    try:
946 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
947 899d2a81 Michael Hanselmann
      try:
948 899d2a81 Michael Hanselmann
        for line in f:
949 899d2a81 Michael Hanselmann
          fields = line.split()
950 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
951 899d2a81 Michael Hanselmann
            names = fields[1:]
952 899d2a81 Michael Hanselmann
            if hostname in names:
953 899d2a81 Michael Hanselmann
              while hostname in names:
954 899d2a81 Michael Hanselmann
                names.remove(hostname)
955 899d2a81 Michael Hanselmann
              if names:
956 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
957 899d2a81 Michael Hanselmann
              continue
958 899d2a81 Michael Hanselmann
959 899d2a81 Michael Hanselmann
          out.write(line)
960 59f82e3f Michael Hanselmann
961 59f82e3f Michael Hanselmann
        out.flush()
962 2e3e75b7 Michael Hanselmann
        os.fsync(out)
963 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
964 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
965 59f82e3f Michael Hanselmann
      finally:
966 59f82e3f Michael Hanselmann
        f.close()
967 a8083063 Iustin Pop
    finally:
968 59f82e3f Michael Hanselmann
      out.close()
969 59f82e3f Michael Hanselmann
  except:
970 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
971 59f82e3f Michael Hanselmann
    raise
972 a8083063 Iustin Pop
973 a8083063 Iustin Pop
974 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
975 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
976 d9c02ca6 Michael Hanselmann

977 58885d79 Iustin Pop
  @type hostname: str
978 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
979 58885d79 Iustin Pop
      full and shot name will be removed from
980 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
981 58885d79 Iustin Pop

982 d9c02ca6 Michael Hanselmann
  """
983 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
984 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
985 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
986 d9c02ca6 Michael Hanselmann
987 d9c02ca6 Michael Hanselmann
988 a8083063 Iustin Pop
def CreateBackup(file_name):
989 a8083063 Iustin Pop
  """Creates a backup of a file.
990 a8083063 Iustin Pop

991 58885d79 Iustin Pop
  @type file_name: str
992 58885d79 Iustin Pop
  @param file_name: file to be backed up
993 58885d79 Iustin Pop
  @rtype: str
994 58885d79 Iustin Pop
  @return: the path to the newly created backup
995 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
996 a8083063 Iustin Pop

997 a8083063 Iustin Pop
  """
998 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
999 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1000 3ecf6786 Iustin Pop
                                file_name)
1001 a8083063 Iustin Pop
1002 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
1003 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1004 081b1e69 Michael Hanselmann
1005 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1006 081b1e69 Michael Hanselmann
  try:
1007 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1008 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1009 081b1e69 Michael Hanselmann
    try:
1010 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1011 081b1e69 Michael Hanselmann
    finally:
1012 081b1e69 Michael Hanselmann
      fdst.close()
1013 081b1e69 Michael Hanselmann
  finally:
1014 081b1e69 Michael Hanselmann
    fsrc.close()
1015 081b1e69 Michael Hanselmann
1016 a8083063 Iustin Pop
  return backup_name
1017 a8083063 Iustin Pop
1018 a8083063 Iustin Pop
1019 a8083063 Iustin Pop
def ShellQuote(value):
1020 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1021 3ecf6786 Iustin Pop

1022 58885d79 Iustin Pop
  @type value: str
1023 58885d79 Iustin Pop
  @param value: the argument to be quoted
1024 58885d79 Iustin Pop
  @rtype: str
1025 58885d79 Iustin Pop
  @return: the quoted value
1026 58885d79 Iustin Pop

1027 a8083063 Iustin Pop
  """
1028 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1029 a8083063 Iustin Pop
    return value
1030 a8083063 Iustin Pop
  else:
1031 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1032 a8083063 Iustin Pop
1033 a8083063 Iustin Pop
1034 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1035 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1036 58885d79 Iustin Pop

1037 58885d79 Iustin Pop
  @type args: list
1038 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1039 58885d79 Iustin Pop
  @rtype: str
1040 58885d79 Iustin Pop
  @return: the quoted arguments concatenaned with spaces
1041 a8083063 Iustin Pop

1042 a8083063 Iustin Pop
  """
1043 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1044 88d14415 Michael Hanselmann
1045 88d14415 Michael Hanselmann
1046 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1047 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1048 2c30e9d7 Alexander Schreiber

1049 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1050 58885d79 Iustin Pop
  to it.
1051 58885d79 Iustin Pop

1052 58885d79 Iustin Pop
  @type target: str
1053 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1054 58885d79 Iustin Pop
  @type port: int
1055 58885d79 Iustin Pop
  @param port: the port to connect to
1056 58885d79 Iustin Pop
  @type timeout: int
1057 58885d79 Iustin Pop
  @param timeout: the timeout on the connection attemp
1058 58885d79 Iustin Pop
  @type live_port_needed: boolean
1059 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1060 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1061 58885d79 Iustin Pop
  @type source: str or None
1062 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1063 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1064 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1065 2c30e9d7 Alexander Schreiber

1066 2c30e9d7 Alexander Schreiber
  """
1067 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1068 2c30e9d7 Alexander Schreiber
1069 0b5ad33e Iustin Pop
  success = False
1070 2c30e9d7 Alexander Schreiber
1071 b15d625f Iustin Pop
  if source is not None:
1072 b15d625f Iustin Pop
    try:
1073 b15d625f Iustin Pop
      sock.bind((source, 0))
1074 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
1075 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1076 b15d625f Iustin Pop
        success = False
1077 2c30e9d7 Alexander Schreiber
1078 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1079 2c30e9d7 Alexander Schreiber
1080 2c30e9d7 Alexander Schreiber
  try:
1081 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1082 2c30e9d7 Alexander Schreiber
    sock.close()
1083 2c30e9d7 Alexander Schreiber
    success = True
1084 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1085 2c30e9d7 Alexander Schreiber
    success = False
1086 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
1087 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1088 2c30e9d7 Alexander Schreiber
1089 2c30e9d7 Alexander Schreiber
  return success
1090 eedbda4b Michael Hanselmann
1091 eedbda4b Michael Hanselmann
1092 caad16e2 Iustin Pop
def OwnIpAddress(address):
1093 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1094 caad16e2 Iustin Pop

1095 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1096 caad16e2 Iustin Pop
  address.
1097 caad16e2 Iustin Pop

1098 caad16e2 Iustin Pop
  @type address: string
1099 caad16e2 Iustin Pop
  @param address: the addres to check
1100 caad16e2 Iustin Pop
  @rtype: bool
1101 58885d79 Iustin Pop
  @return: True if we own the address
1102 caad16e2 Iustin Pop

1103 caad16e2 Iustin Pop
  """
1104 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1105 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1106 caad16e2 Iustin Pop
1107 caad16e2 Iustin Pop
1108 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1109 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1110 58885d79 Iustin Pop

1111 58885d79 Iustin Pop
  @type path: str
1112 58885d79 Iustin Pop
  @param path: the directory to enumerate
1113 58885d79 Iustin Pop
  @rtype: list
1114 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1115 eedbda4b Michael Hanselmann

1116 eedbda4b Michael Hanselmann
  """
1117 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1118 f3299a07 Michael Hanselmann
  files.sort()
1119 f3299a07 Michael Hanselmann
  return files
1120 2f8b60b3 Iustin Pop
1121 2f8b60b3 Iustin Pop
1122 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1123 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1124 257f4c0a Iustin Pop

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

1129 2f8b60b3 Iustin Pop
  """
1130 2f8b60b3 Iustin Pop
  try:
1131 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1132 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1133 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1134 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1135 257f4c0a Iustin Pop
    else:
1136 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1137 257f4c0a Iustin Pop
                                   type(user))
1138 2f8b60b3 Iustin Pop
  except KeyError:
1139 2f8b60b3 Iustin Pop
    return default
1140 2f8b60b3 Iustin Pop
  return result.pw_dir
1141 59072e7e Michael Hanselmann
1142 59072e7e Michael Hanselmann
1143 24818e8f Michael Hanselmann
def NewUUID():
1144 59072e7e Michael Hanselmann
  """Returns a random UUID.
1145 59072e7e Michael Hanselmann

1146 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1147 58885d79 Iustin Pop
      filesystem.
1148 58885d79 Iustin Pop
  @rtype: str
1149 58885d79 Iustin Pop

1150 59072e7e Michael Hanselmann
  """
1151 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
1152 59072e7e Michael Hanselmann
  try:
1153 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
1154 59072e7e Michael Hanselmann
  finally:
1155 59072e7e Michael Hanselmann
    f.close()
1156 087b34fe Iustin Pop
1157 087b34fe Iustin Pop
1158 33081d90 Iustin Pop
def GenerateSecret():
1159 33081d90 Iustin Pop
  """Generates a random secret.
1160 33081d90 Iustin Pop

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

1164 58885d79 Iustin Pop
  @rtype: str
1165 58885d79 Iustin Pop
  @return: a sha1 hexdigest of a block of 64 random bytes
1166 58885d79 Iustin Pop

1167 33081d90 Iustin Pop
  """
1168 7ffe8fba Carlos Valiente
  return sha1(os.urandom(64)).hexdigest()
1169 33081d90 Iustin Pop
1170 33081d90 Iustin Pop
1171 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1172 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1173 9dae41ad Guido Trotter

1174 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1175 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1176 9dae41ad Guido Trotter

1177 9dae41ad Guido Trotter
  """
1178 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1179 9dae41ad Guido Trotter
    try:
1180 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1181 9dae41ad Guido Trotter
    except EnvironmentError, err:
1182 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1183 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1184 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1185 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1186 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1187 9dae41ad Guido Trotter
1188 9dae41ad Guido Trotter
1189 ca0aa6d0 Michael Hanselmann
def ReadFile(file_name, size=None):
1190 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1191 ca0aa6d0 Michael Hanselmann

1192 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1193 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1194 58885d79 Iustin Pop
  @rtype: str
1195 58885d79 Iustin Pop
  @return: the (possibly partial) conent of the file
1196 ca0aa6d0 Michael Hanselmann

1197 ca0aa6d0 Michael Hanselmann
  """
1198 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1199 ca0aa6d0 Michael Hanselmann
  try:
1200 ca0aa6d0 Michael Hanselmann
    if size is None:
1201 ca0aa6d0 Michael Hanselmann
      return f.read()
1202 ca0aa6d0 Michael Hanselmann
    else:
1203 ca0aa6d0 Michael Hanselmann
      return f.read(size)
1204 ca0aa6d0 Michael Hanselmann
  finally:
1205 ca0aa6d0 Michael Hanselmann
    f.close()
1206 ca0aa6d0 Michael Hanselmann
1207 ca0aa6d0 Michael Hanselmann
1208 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1209 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1210 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1211 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1212 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1213 087b34fe Iustin Pop
  """(Over)write a file atomically.
1214 087b34fe Iustin Pop

1215 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1216 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1217 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1218 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1219 087b34fe Iustin Pop
  mtime/atime of the file.
1220 087b34fe Iustin Pop

1221 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1222 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1223 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1224 087b34fe Iustin Pop
  temporary file should be removed.
1225 087b34fe Iustin Pop

1226 58885d79 Iustin Pop
  @type file_name: str
1227 58885d79 Iustin Pop
  @param file_name: the target filename
1228 58885d79 Iustin Pop
  @type fn: callable
1229 58885d79 Iustin Pop
  @param fn: content writing function, called with
1230 58885d79 Iustin Pop
      file descriptor as parameter
1231 69efe319 Michael Hanselmann
  @type data: str
1232 58885d79 Iustin Pop
  @param data: contents of the file
1233 58885d79 Iustin Pop
  @type mode: int
1234 58885d79 Iustin Pop
  @param mode: file mode
1235 58885d79 Iustin Pop
  @type uid: int
1236 58885d79 Iustin Pop
  @param uid: the owner of the file
1237 58885d79 Iustin Pop
  @type gid: int
1238 58885d79 Iustin Pop
  @param gid: the group of the file
1239 58885d79 Iustin Pop
  @type atime: int
1240 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1241 58885d79 Iustin Pop
  @type mtime: int
1242 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1243 58885d79 Iustin Pop
  @type close: boolean
1244 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1245 58885d79 Iustin Pop
  @type prewrite: callable
1246 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1247 58885d79 Iustin Pop
  @type postwrite: callable
1248 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1249 58885d79 Iustin Pop

1250 58885d79 Iustin Pop
  @rtype: None or int
1251 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1252 58885d79 Iustin Pop
      otherwise the file descriptor
1253 58885d79 Iustin Pop

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

1256 087b34fe Iustin Pop
  """
1257 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1258 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1259 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1260 087b34fe Iustin Pop
1261 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1262 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1263 087b34fe Iustin Pop
1264 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1265 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1266 087b34fe Iustin Pop
                                 " set or None")
1267 087b34fe Iustin Pop
1268 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1269 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1270 087b34fe Iustin Pop
1271 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1272 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1273 81b7354c Iustin Pop
  do_remove = True
1274 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1275 087b34fe Iustin Pop
  # leaves it in place
1276 087b34fe Iustin Pop
  try:
1277 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1278 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1279 087b34fe Iustin Pop
    if mode:
1280 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1281 71714516 Michael Hanselmann
    if callable(prewrite):
1282 71714516 Michael Hanselmann
      prewrite(fd)
1283 087b34fe Iustin Pop
    if data is not None:
1284 087b34fe Iustin Pop
      os.write(fd, data)
1285 087b34fe Iustin Pop
    else:
1286 087b34fe Iustin Pop
      fn(fd)
1287 71714516 Michael Hanselmann
    if callable(postwrite):
1288 71714516 Michael Hanselmann
      postwrite(fd)
1289 087b34fe Iustin Pop
    os.fsync(fd)
1290 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1291 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1292 70f4497c Michael Hanselmann
    if not dry_run:
1293 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1294 81b7354c Iustin Pop
      do_remove = False
1295 087b34fe Iustin Pop
  finally:
1296 71714516 Michael Hanselmann
    if close:
1297 71714516 Michael Hanselmann
      os.close(fd)
1298 71714516 Michael Hanselmann
      result = None
1299 71714516 Michael Hanselmann
    else:
1300 71714516 Michael Hanselmann
      result = fd
1301 81b7354c Iustin Pop
    if do_remove:
1302 81b7354c Iustin Pop
      RemoveFile(new_name)
1303 78feb6fb Guido Trotter
1304 71714516 Michael Hanselmann
  return result
1305 71714516 Michael Hanselmann
1306 78feb6fb Guido Trotter
1307 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1308 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1309 7b4126b7 Iustin Pop

1310 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1311 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1312 7b4126b7 Iustin Pop
  value, the index will be returned.
1313 7b4126b7 Iustin Pop

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

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

1319 58885d79 Iustin Pop
  @type seq: sequence
1320 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1321 58885d79 Iustin Pop
  @type base: int
1322 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1323 58885d79 Iustin Pop
  @rtype: int
1324 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1325 7b4126b7 Iustin Pop

1326 7b4126b7 Iustin Pop
  """
1327 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1328 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1329 7b4126b7 Iustin Pop
    if elem > idx + base:
1330 7b4126b7 Iustin Pop
      # idx is not used
1331 7b4126b7 Iustin Pop
      return idx + base
1332 7b4126b7 Iustin Pop
  return None
1333 7b4126b7 Iustin Pop
1334 7b4126b7 Iustin Pop
1335 78feb6fb Guido Trotter
def all(seq, pred=bool):
1336 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1337 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
1338 78feb6fb Guido Trotter
    return False
1339 78feb6fb Guido Trotter
  return True
1340 78feb6fb Guido Trotter
1341 78feb6fb Guido Trotter
1342 78feb6fb Guido Trotter
def any(seq, pred=bool):
1343 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1344 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
1345 78feb6fb Guido Trotter
    return True
1346 78feb6fb Guido Trotter
  return False
1347 f7414041 Michael Hanselmann
1348 f7414041 Michael Hanselmann
1349 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1350 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1351 f7414041 Michael Hanselmann

1352 f7414041 Michael Hanselmann
  Element order is preserved.
1353 58885d79 Iustin Pop

1354 58885d79 Iustin Pop
  @type seq: sequence
1355 58885d79 Iustin Pop
  @param seq: the sequence with the source elementes
1356 58885d79 Iustin Pop
  @rtype: list
1357 58885d79 Iustin Pop
  @return: list of unique elements from seq
1358 58885d79 Iustin Pop

1359 f7414041 Michael Hanselmann
  """
1360 f7414041 Michael Hanselmann
  seen = set()
1361 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1362 1862d460 Alexander Schreiber
1363 1862d460 Alexander Schreiber
1364 1862d460 Alexander Schreiber
def IsValidMac(mac):
1365 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1366 1862d460 Alexander Schreiber

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

1370 58885d79 Iustin Pop
  @type mac: str
1371 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1372 58885d79 Iustin Pop
  @rtype: boolean
1373 58885d79 Iustin Pop
  @return: True is the MAC seems valid
1374 58885d79 Iustin Pop

1375 1862d460 Alexander Schreiber
  """
1376 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1377 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1378 06009e27 Iustin Pop
1379 06009e27 Iustin Pop
1380 06009e27 Iustin Pop
def TestDelay(duration):
1381 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1382 06009e27 Iustin Pop

1383 58885d79 Iustin Pop
  @type duration: float
1384 58885d79 Iustin Pop
  @param duration: the sleep duration
1385 58885d79 Iustin Pop
  @rtype: boolean
1386 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1387 58885d79 Iustin Pop

1388 06009e27 Iustin Pop
  """
1389 06009e27 Iustin Pop
  if duration < 0:
1390 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1391 06009e27 Iustin Pop
  time.sleep(duration)
1392 38ea42a1 Iustin Pop
  return True, None
1393 8f765069 Iustin Pop
1394 8f765069 Iustin Pop
1395 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1396 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1397 8f765069 Iustin Pop

1398 7d88772a Iustin Pop
  @type fd: int
1399 7d88772a Iustin Pop
  @param fd: the file descriptor
1400 7d88772a Iustin Pop
  @type retries: int
1401 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1402 7d88772a Iustin Pop
      other error than EBADF
1403 7d88772a Iustin Pop

1404 7d88772a Iustin Pop
  """
1405 7d88772a Iustin Pop
  try:
1406 7d88772a Iustin Pop
    os.close(fd)
1407 7d88772a Iustin Pop
  except OSError, err:
1408 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1409 7d88772a Iustin Pop
      if retries > 0:
1410 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1411 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1412 7d88772a Iustin Pop
    # ignore this and go on
1413 7d88772a Iustin Pop
1414 7d88772a Iustin Pop
1415 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1416 7d88772a Iustin Pop
  """Close file descriptors.
1417 7d88772a Iustin Pop

1418 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1419 7d88772a Iustin Pop
  stdin/out/err).
1420 8f765069 Iustin Pop

1421 58885d79 Iustin Pop
  @type noclose_fds: list or None
1422 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1423 58885d79 Iustin Pop
      that should not be closed
1424 58885d79 Iustin Pop

1425 8f765069 Iustin Pop
  """
1426 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1427 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1428 8f765069 Iustin Pop
    try:
1429 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1430 8f765069 Iustin Pop
      if MAXFD < 0:
1431 8f765069 Iustin Pop
        MAXFD = 1024
1432 8f765069 Iustin Pop
    except OSError:
1433 8f765069 Iustin Pop
      MAXFD = 1024
1434 8f765069 Iustin Pop
  else:
1435 8f765069 Iustin Pop
    MAXFD = 1024
1436 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1437 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1438 7d88772a Iustin Pop
    maxfd = MAXFD
1439 7d88772a Iustin Pop
1440 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1441 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1442 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1443 7d88772a Iustin Pop
      continue
1444 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1445 7d88772a Iustin Pop
1446 7d88772a Iustin Pop
1447 7d88772a Iustin Pop
def Daemonize(logfile):
1448 7d88772a Iustin Pop
  """Daemonize the current process.
1449 7d88772a Iustin Pop

1450 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1451 7d88772a Iustin Pop
  runs it in the background as a daemon.
1452 7d88772a Iustin Pop

1453 7d88772a Iustin Pop
  @type logfile: str
1454 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1455 7d88772a Iustin Pop
  @rtype: int
1456 5fcc718f Iustin Pop
  @return: the value zero
1457 7d88772a Iustin Pop

1458 7d88772a Iustin Pop
  """
1459 7d88772a Iustin Pop
  UMASK = 077
1460 7d88772a Iustin Pop
  WORKDIR = "/"
1461 8f765069 Iustin Pop
1462 8f765069 Iustin Pop
  # this might fail
1463 8f765069 Iustin Pop
  pid = os.fork()
1464 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1465 8f765069 Iustin Pop
    os.setsid()
1466 8f765069 Iustin Pop
    # this might fail
1467 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1468 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1469 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1470 8f765069 Iustin Pop
      os.umask(UMASK)
1471 8f765069 Iustin Pop
    else:
1472 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1473 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1474 8f765069 Iustin Pop
  else:
1475 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1476 8f765069 Iustin Pop
1477 7d88772a Iustin Pop
  for fd in range(3):
1478 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1479 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1480 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1481 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1482 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1483 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1484 7d88772a Iustin Pop
  os.dup2(1, 2)
1485 8f765069 Iustin Pop
  return 0
1486 57c177af Iustin Pop
1487 57c177af Iustin Pop
1488 53beffbb Iustin Pop
def DaemonPidFileName(name):
1489 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1490 58885d79 Iustin Pop

1491 58885d79 Iustin Pop
  @type name: str
1492 58885d79 Iustin Pop
  @param name: the daemon name
1493 58885d79 Iustin Pop
  @rtype: str
1494 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1495 58885d79 Iustin Pop
      daemon name
1496 b330ac0b Guido Trotter

1497 b330ac0b Guido Trotter
  """
1498 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1499 b330ac0b Guido Trotter
1500 b330ac0b Guido Trotter
1501 b330ac0b Guido Trotter
def WritePidFile(name):
1502 b330ac0b Guido Trotter
  """Write the current process pidfile.
1503 b330ac0b Guido Trotter

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

1506 58885d79 Iustin Pop
  @type name: str
1507 58885d79 Iustin Pop
  @param name: the daemon name to use
1508 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1509 58885d79 Iustin Pop
      points to a live process
1510 b330ac0b Guido Trotter

1511 b330ac0b Guido Trotter
  """
1512 b330ac0b Guido Trotter
  pid = os.getpid()
1513 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1514 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1515 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1516 b330ac0b Guido Trotter
1517 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1518 b330ac0b Guido Trotter
1519 b330ac0b Guido Trotter
1520 b330ac0b Guido Trotter
def RemovePidFile(name):
1521 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1522 b330ac0b Guido Trotter

1523 b330ac0b Guido Trotter
  Any errors are ignored.
1524 b330ac0b Guido Trotter

1525 58885d79 Iustin Pop
  @type name: str
1526 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1527 58885d79 Iustin Pop

1528 b330ac0b Guido Trotter
  """
1529 b330ac0b Guido Trotter
  pid = os.getpid()
1530 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1531 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1532 b330ac0b Guido Trotter
  try:
1533 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1534 b330ac0b Guido Trotter
  except:
1535 b330ac0b Guido Trotter
    pass
1536 b330ac0b Guido Trotter
1537 b330ac0b Guido Trotter
1538 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1539 ff5251bc Iustin Pop
                waitpid=False):
1540 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1541 b2a1f511 Iustin Pop

1542 b2a1f511 Iustin Pop
  @type pid: int
1543 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1544 38206f3c Iustin Pop
  @type signal_: int
1545 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1546 b2a1f511 Iustin Pop
  @type timeout: int
1547 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1548 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1549 b2a1f511 Iustin Pop
                  will be done
1550 ff5251bc Iustin Pop
  @type waitpid: boolean
1551 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1552 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1553 ff5251bc Iustin Pop
      would remain as zombie
1554 b2a1f511 Iustin Pop

1555 b2a1f511 Iustin Pop
  """
1556 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1557 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1558 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1559 ff5251bc Iustin Pop
    if wait:
1560 ff5251bc Iustin Pop
      try:
1561 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1562 ff5251bc Iustin Pop
      except OSError:
1563 ff5251bc Iustin Pop
        pass
1564 ff5251bc Iustin Pop
1565 b2a1f511 Iustin Pop
  if pid <= 0:
1566 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1567 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1568 b2a1f511 Iustin Pop
1569 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1570 b2a1f511 Iustin Pop
    return
1571 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1572 b2a1f511 Iustin Pop
  if timeout <= 0:
1573 b2a1f511 Iustin Pop
    return
1574 7167159a Michael Hanselmann
1575 7167159a Michael Hanselmann
  # Wait up to $timeout seconds
1576 b2a1f511 Iustin Pop
  end = time.time() + timeout
1577 7167159a Michael Hanselmann
  wait = 0.01
1578 b2a1f511 Iustin Pop
  while time.time() < end and IsProcessAlive(pid):
1579 7167159a Michael Hanselmann
    try:
1580 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1581 7167159a Michael Hanselmann
      if result_pid > 0:
1582 7167159a Michael Hanselmann
        break
1583 7167159a Michael Hanselmann
    except OSError:
1584 7167159a Michael Hanselmann
      pass
1585 7167159a Michael Hanselmann
    time.sleep(wait)
1586 7167159a Michael Hanselmann
    # Make wait time longer for next try
1587 7167159a Michael Hanselmann
    if wait < 0.1:
1588 7167159a Michael Hanselmann
      wait *= 1.5
1589 7167159a Michael Hanselmann
1590 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1591 7167159a Michael Hanselmann
    # Kill process if it's still alive
1592 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1593 b2a1f511 Iustin Pop
1594 b2a1f511 Iustin Pop
1595 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1596 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1597 57c177af Iustin Pop

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

1601 58885d79 Iustin Pop
  @type name: str
1602 58885d79 Iustin Pop
  @param name: the name to look for
1603 58885d79 Iustin Pop
  @type search_path: str
1604 58885d79 Iustin Pop
  @param search_path: location to start at
1605 58885d79 Iustin Pop
  @type test: callable
1606 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1607 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1608 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1609 58885d79 Iustin Pop
  @rtype: str or None
1610 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1611 57c177af Iustin Pop

1612 57c177af Iustin Pop
  """
1613 57c177af Iustin Pop
  for dir_name in search_path:
1614 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1615 57c177af Iustin Pop
    if test(item_name):
1616 57c177af Iustin Pop
      return item_name
1617 57c177af Iustin Pop
  return None
1618 8d1a2a64 Michael Hanselmann
1619 8d1a2a64 Michael Hanselmann
1620 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1621 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1622 8d1a2a64 Michael Hanselmann

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

1626 58885d79 Iustin Pop
  @type vglist: dict
1627 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1628 58885d79 Iustin Pop
  @type vgname: str
1629 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1630 58885d79 Iustin Pop
  @type minsize: int
1631 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1632 58885d79 Iustin Pop
  @rtype: None or str
1633 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1634 8d1a2a64 Michael Hanselmann

1635 8d1a2a64 Michael Hanselmann
  """
1636 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1637 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1638 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1639 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1640 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1641 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1642 8d1a2a64 Michael Hanselmann
  return None
1643 7996a135 Iustin Pop
1644 7996a135 Iustin Pop
1645 45bc5e4a Michael Hanselmann
def SplitTime(value):
1646 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1647 739be818 Michael Hanselmann

1648 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1649 45bc5e4a Michael Hanselmann
  @type value: int or float
1650 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1651 739be818 Michael Hanselmann

1652 739be818 Michael Hanselmann
  """
1653 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1654 45bc5e4a Michael Hanselmann
1655 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1656 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1657 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1658 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1659 45bc5e4a Michael Hanselmann
1660 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1661 739be818 Michael Hanselmann
1662 739be818 Michael Hanselmann
1663 739be818 Michael Hanselmann
def MergeTime(timetuple):
1664 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1665 739be818 Michael Hanselmann

1666 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1667 739be818 Michael Hanselmann
  @type timetuple: tuple
1668 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1669 739be818 Michael Hanselmann

1670 739be818 Michael Hanselmann
  """
1671 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1672 739be818 Michael Hanselmann
1673 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1674 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1675 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1676 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1677 739be818 Michael Hanselmann
1678 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1679 739be818 Michael Hanselmann
1680 739be818 Michael Hanselmann
1681 4a8b186a Michael Hanselmann
def GetNodeDaemonPort():
1682 4a8b186a Michael Hanselmann
  """Get the node daemon port for this cluster.
1683 4a8b186a Michael Hanselmann

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

1688 58885d79 Iustin Pop
  @rtype: int
1689 58885d79 Iustin Pop

1690 4a8b186a Michael Hanselmann
  """
1691 4a8b186a Michael Hanselmann
  try:
1692 4a8b186a Michael Hanselmann
    port = socket.getservbyname("ganeti-noded", "tcp")
1693 4a8b186a Michael Hanselmann
  except socket.error:
1694 4a8b186a Michael Hanselmann
    port = constants.DEFAULT_NODED_PORT
1695 4a8b186a Michael Hanselmann
1696 4a8b186a Michael Hanselmann
  return port
1697 4a8b186a Michael Hanselmann
1698 4a8b186a Michael Hanselmann
1699 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1700 d21d09d6 Iustin Pop
                 multithreaded=False):
1701 82d9caef Iustin Pop
  """Configures the logging module.
1702 82d9caef Iustin Pop

1703 58885d79 Iustin Pop
  @type logfile: str
1704 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1705 58885d79 Iustin Pop
  @type debug: boolean
1706 58885d79 Iustin Pop
  @param debug: whether to enable debug messages too or
1707 58885d79 Iustin Pop
      only those at C{INFO} and above level
1708 58885d79 Iustin Pop
  @type stderr_logging: boolean
1709 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1710 58885d79 Iustin Pop
  @type program: str
1711 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1712 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1713 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1714 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1715 58885d79 Iustin Pop
      stderr logging is disabled
1716 58885d79 Iustin Pop

1717 82d9caef Iustin Pop
  """
1718 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1719 d21d09d6 Iustin Pop
  if multithreaded:
1720 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1721 82d9caef Iustin Pop
  if debug:
1722 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1723 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1724 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1725 82d9caef Iustin Pop
1726 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1727 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1728 82d9caef Iustin Pop
1729 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1730 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1731 7d88772a Iustin Pop
    handler.close()
1732 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1733 6346a9e5 Michael Hanselmann
1734 82d9caef Iustin Pop
  if stderr_logging:
1735 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1736 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1737 82d9caef Iustin Pop
    if debug:
1738 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1739 82d9caef Iustin Pop
    else:
1740 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1741 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1742 82d9caef Iustin Pop
1743 82d9caef Iustin Pop
  # this can fail, if the logging directories are not setup or we have
1744 82d9caef Iustin Pop
  # a permisssion problem; in this case, it's best to log but ignore
1745 82d9caef Iustin Pop
  # the error if stderr_logging is True, and if false we re-raise the
1746 82d9caef Iustin Pop
  # exception since otherwise we could run but without any logs at all
1747 82d9caef Iustin Pop
  try:
1748 82d9caef Iustin Pop
    logfile_handler = logging.FileHandler(logfile)
1749 82d9caef Iustin Pop
    logfile_handler.setFormatter(formatter)
1750 82d9caef Iustin Pop
    if debug:
1751 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.DEBUG)
1752 82d9caef Iustin Pop
    else:
1753 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.INFO)
1754 82d9caef Iustin Pop
    root_logger.addHandler(logfile_handler)
1755 d21d09d6 Iustin Pop
  except EnvironmentError:
1756 82d9caef Iustin Pop
    if stderr_logging:
1757 82d9caef Iustin Pop
      logging.exception("Failed to enable logging to file '%s'", logfile)
1758 82d9caef Iustin Pop
    else:
1759 82d9caef Iustin Pop
      # we need to re-raise the exception
1760 82d9caef Iustin Pop
      raise
1761 82d9caef Iustin Pop
1762 da961187 Guido Trotter
def IsNormAbsPath(path):
1763 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
1764 da961187 Guido Trotter

1765 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1766 da961187 Guido Trotter

1767 da961187 Guido Trotter
  """
1768 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1769 82d9caef Iustin Pop
1770 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1771 f65f63ef Iustin Pop
  """Return the last lines from a file.
1772 f65f63ef Iustin Pop

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

1777 f65f63ef Iustin Pop
  @param fname: the file name
1778 f65f63ef Iustin Pop
  @type lines: int
1779 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1780 f65f63ef Iustin Pop

1781 f65f63ef Iustin Pop
  """
1782 f65f63ef Iustin Pop
  fd = open(fname, "r")
1783 f65f63ef Iustin Pop
  try:
1784 f65f63ef Iustin Pop
    fd.seek(0, 2)
1785 f65f63ef Iustin Pop
    pos = fd.tell()
1786 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1787 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1788 f65f63ef Iustin Pop
    raw_data = fd.read()
1789 f65f63ef Iustin Pop
  finally:
1790 f65f63ef Iustin Pop
    fd.close()
1791 f65f63ef Iustin Pop
1792 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1793 f65f63ef Iustin Pop
  return rows[-lines:]
1794 f65f63ef Iustin Pop
1795 f65f63ef Iustin Pop
1796 26f15862 Iustin Pop
def SafeEncode(text):
1797 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1798 26f15862 Iustin Pop

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

1806 26f15862 Iustin Pop
  @type text: str or unicode
1807 26f15862 Iustin Pop
  @param text: input data
1808 26f15862 Iustin Pop
  @rtype: str
1809 26f15862 Iustin Pop
  @return: a safe version of text
1810 26f15862 Iustin Pop

1811 26f15862 Iustin Pop
  """
1812 26f15862 Iustin Pop
  text = text.encode('ascii', 'backslashreplace')
1813 26f15862 Iustin Pop
  text = text.encode('string_escape')
1814 26f15862 Iustin Pop
  return text
1815 26f15862 Iustin Pop
1816 26f15862 Iustin Pop
1817 835528af Iustin Pop
def CommaJoin(names):
1818 835528af Iustin Pop
  """Nicely join a set of identifiers.
1819 835528af Iustin Pop

1820 835528af Iustin Pop
  @param names: set, list or tuple
1821 835528af Iustin Pop
  @return: a string with the formatted results
1822 835528af Iustin Pop

1823 835528af Iustin Pop
  """
1824 835528af Iustin Pop
  return ", ".join(["'%s'" % val for val in names])
1825 835528af Iustin Pop
1826 835528af Iustin Pop
1827 7996a135 Iustin Pop
def LockedMethod(fn):
1828 7996a135 Iustin Pop
  """Synchronized object access decorator.
1829 7996a135 Iustin Pop

1830 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1831 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1832 7996a135 Iustin Pop

1833 7996a135 Iustin Pop
  """
1834 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1835 e67bd559 Michael Hanselmann
    if debug_locks:
1836 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1837 e67bd559 Michael Hanselmann
1838 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1839 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1840 7996a135 Iustin Pop
    lock = self._lock
1841 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1842 7996a135 Iustin Pop
    lock.acquire()
1843 7996a135 Iustin Pop
    try:
1844 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1845 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1846 7996a135 Iustin Pop
    finally:
1847 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1848 7996a135 Iustin Pop
      lock.release()
1849 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1850 7996a135 Iustin Pop
    return result
1851 7996a135 Iustin Pop
  return wrapper
1852 eb0f0ce0 Michael Hanselmann
1853 eb0f0ce0 Michael Hanselmann
1854 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1855 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1856 eb0f0ce0 Michael Hanselmann

1857 58885d79 Iustin Pop
  @type fd: int
1858 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1859 58885d79 Iustin Pop

1860 eb0f0ce0 Michael Hanselmann
  """
1861 eb0f0ce0 Michael Hanselmann
  try:
1862 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1863 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1864 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1865 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1866 eb0f0ce0 Michael Hanselmann
    raise
1867 de499029 Michael Hanselmann
1868 de499029 Michael Hanselmann
1869 a87b4824 Michael Hanselmann
class FileLock(object):
1870 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1871 a87b4824 Michael Hanselmann

1872 a87b4824 Michael Hanselmann
  """
1873 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1874 58885d79 Iustin Pop
    """Constructor for FileLock.
1875 58885d79 Iustin Pop

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

1878 58885d79 Iustin Pop
    @type filename: str
1879 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1880 58885d79 Iustin Pop

1881 58885d79 Iustin Pop
    """
1882 a87b4824 Michael Hanselmann
    self.filename = filename
1883 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1884 a87b4824 Michael Hanselmann
1885 a87b4824 Michael Hanselmann
  def __del__(self):
1886 a87b4824 Michael Hanselmann
    self.Close()
1887 a87b4824 Michael Hanselmann
1888 a87b4824 Michael Hanselmann
  def Close(self):
1889 58885d79 Iustin Pop
    """Close the file and release the lock.
1890 58885d79 Iustin Pop

1891 58885d79 Iustin Pop
    """
1892 a87b4824 Michael Hanselmann
    if self.fd:
1893 a87b4824 Michael Hanselmann
      self.fd.close()
1894 a87b4824 Michael Hanselmann
      self.fd = None
1895 a87b4824 Michael Hanselmann
1896 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
1897 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
1898 aa74b828 Michael Hanselmann

1899 aa74b828 Michael Hanselmann
    @type flag: int
1900 58885d79 Iustin Pop
    @param flag: operation flag
1901 aa74b828 Michael Hanselmann
    @type blocking: bool
1902 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
1903 aa74b828 Michael Hanselmann
    @type timeout: None or float
1904 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
1905 aa74b828 Michael Hanselmann
                    non-blocking mode).
1906 aa74b828 Michael Hanselmann
    @type errmsg: string
1907 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
1908 aa74b828 Michael Hanselmann

1909 aa74b828 Michael Hanselmann
    """
1910 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1911 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
1912 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
1913 a87b4824 Michael Hanselmann
1914 aa74b828 Michael Hanselmann
    if timeout is not None:
1915 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1916 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
1917 a87b4824 Michael Hanselmann
1918 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
1919 aa74b828 Michael Hanselmann
    elif not blocking:
1920 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1921 aa74b828 Michael Hanselmann
      timeout_end = None
1922 aa74b828 Michael Hanselmann
1923 aa74b828 Michael Hanselmann
    retry = True
1924 aa74b828 Michael Hanselmann
    while retry:
1925 aa74b828 Michael Hanselmann
      try:
1926 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
1927 aa74b828 Michael Hanselmann
        retry = False
1928 aa74b828 Michael Hanselmann
      except IOError, err:
1929 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
1930 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
1931 aa74b828 Michael Hanselmann
            # Wait before trying again
1932 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
1933 aa74b828 Michael Hanselmann
          else:
1934 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
1935 aa74b828 Michael Hanselmann
        else:
1936 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
1937 aa74b828 Michael Hanselmann
          raise
1938 aa74b828 Michael Hanselmann
1939 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
1940 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1941 a87b4824 Michael Hanselmann

1942 58885d79 Iustin Pop
    @type blocking: boolean
1943 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1944 58885d79 Iustin Pop
        can lock the file or return immediately
1945 58885d79 Iustin Pop
    @type timeout: int or None
1946 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1947 58885d79 Iustin Pop
        (in blocking mode)
1948 58885d79 Iustin Pop

1949 a87b4824 Michael Hanselmann
    """
1950 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
1951 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1952 a87b4824 Michael Hanselmann
1953 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
1954 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1955 a87b4824 Michael Hanselmann

1956 58885d79 Iustin Pop
    @type blocking: boolean
1957 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1958 58885d79 Iustin Pop
        can lock the file or return immediately
1959 58885d79 Iustin Pop
    @type timeout: int or None
1960 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1961 58885d79 Iustin Pop
        (in blocking mode)
1962 58885d79 Iustin Pop

1963 a87b4824 Michael Hanselmann
    """
1964 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
1965 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1966 a87b4824 Michael Hanselmann
1967 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
1968 a87b4824 Michael Hanselmann
    """Unlocks the file.
1969 a87b4824 Michael Hanselmann

1970 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
1971 58885d79 Iustin Pop
    operation::
1972 58885d79 Iustin Pop

1973 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
1974 58885d79 Iustin Pop
      operations.
1975 58885d79 Iustin Pop

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

1983 a87b4824 Michael Hanselmann
    """
1984 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
1985 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
1986 a87b4824 Michael Hanselmann
1987 a87b4824 Michael Hanselmann
1988 de499029 Michael Hanselmann
class SignalHandler(object):
1989 de499029 Michael Hanselmann
  """Generic signal handler class.
1990 de499029 Michael Hanselmann

1991 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
1992 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
1993 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
1994 58885d79 Iustin Pop
  signal was sent.
1995 58885d79 Iustin Pop

1996 58885d79 Iustin Pop
  @type signum: list
1997 58885d79 Iustin Pop
  @ivar signum: the signals we handle
1998 58885d79 Iustin Pop
  @type called: boolean
1999 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2000 de499029 Michael Hanselmann

2001 de499029 Michael Hanselmann
  """
2002 de499029 Michael Hanselmann
  def __init__(self, signum):
2003 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2004 de499029 Michael Hanselmann

2005 58885d79 Iustin Pop
    @type signum: int or list of ints
2006 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2007 de499029 Michael Hanselmann

2008 de499029 Michael Hanselmann
    """
2009 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
2010 de499029 Michael Hanselmann
      self.signum = set([signum])
2011 de499029 Michael Hanselmann
    else:
2012 de499029 Michael Hanselmann
      self.signum = set(signum)
2013 de499029 Michael Hanselmann
2014 de499029 Michael Hanselmann
    self.called = False
2015 de499029 Michael Hanselmann
2016 de499029 Michael Hanselmann
    self._previous = {}
2017 de499029 Michael Hanselmann
    try:
2018 de499029 Michael Hanselmann
      for signum in self.signum:
2019 de499029 Michael Hanselmann
        # Setup handler
2020 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2021 de499029 Michael Hanselmann
        try:
2022 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2023 de499029 Michael Hanselmann
        except:
2024 de499029 Michael Hanselmann
          # Restore previous handler
2025 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2026 de499029 Michael Hanselmann
          raise
2027 de499029 Michael Hanselmann
    except:
2028 de499029 Michael Hanselmann
      # Reset all handlers
2029 de499029 Michael Hanselmann
      self.Reset()
2030 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2031 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2032 de499029 Michael Hanselmann
      raise
2033 de499029 Michael Hanselmann
2034 de499029 Michael Hanselmann
  def __del__(self):
2035 de499029 Michael Hanselmann
    self.Reset()
2036 de499029 Michael Hanselmann
2037 de499029 Michael Hanselmann
  def Reset(self):
2038 de499029 Michael Hanselmann
    """Restore previous handler.
2039 de499029 Michael Hanselmann

2040 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2041 58885d79 Iustin Pop

2042 de499029 Michael Hanselmann
    """
2043 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2044 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2045 de499029 Michael Hanselmann
      # If successful, remove from dict
2046 de499029 Michael Hanselmann
      del self._previous[signum]
2047 de499029 Michael Hanselmann
2048 de499029 Michael Hanselmann
  def Clear(self):
2049 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2050 de499029 Michael Hanselmann

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

2053 de499029 Michael Hanselmann
    """
2054 de499029 Michael Hanselmann
    self.called = False
2055 de499029 Michael Hanselmann
2056 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
2057 de499029 Michael Hanselmann
    """Actual signal handling function.
2058 de499029 Michael Hanselmann

2059 de499029 Michael Hanselmann
    """
2060 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2061 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2062 de499029 Michael Hanselmann
    self.called = True
2063 a2d2e1a7 Iustin Pop
2064 a2d2e1a7 Iustin Pop
2065 a2d2e1a7 Iustin Pop
class FieldSet(object):
2066 a2d2e1a7 Iustin Pop
  """A simple field set.
2067 a2d2e1a7 Iustin Pop

2068 a2d2e1a7 Iustin Pop
  Among the features are:
2069 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2070 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2071 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2072 a2d2e1a7 Iustin Pop

2073 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2074 a2d2e1a7 Iustin Pop

2075 a2d2e1a7 Iustin Pop
  """
2076 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2077 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2078 a2d2e1a7 Iustin Pop
2079 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2080 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2081 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2082 a2d2e1a7 Iustin Pop
2083 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2084 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2085 a2d2e1a7 Iustin Pop

2086 a2d2e1a7 Iustin Pop
    @type field: str
2087 a2d2e1a7 Iustin Pop
    @param field: the string to match
2088 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2089 a2d2e1a7 Iustin Pop

2090 a2d2e1a7 Iustin Pop
    """
2091 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2092 a2d2e1a7 Iustin Pop
      return m
2093 a2d2e1a7 Iustin Pop
    return False
2094 a2d2e1a7 Iustin Pop
2095 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2096 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2097 a2d2e1a7 Iustin Pop

2098 a2d2e1a7 Iustin Pop
    @type items: list
2099 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2100 a2d2e1a7 Iustin Pop
    @rtype: list
2101 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2102 a2d2e1a7 Iustin Pop

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