Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 23f06b2b

History | View | Annotate | Download (56.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 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
377 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
378 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

701 58885d79 Iustin Pop
  @type ip: str
702 58885d79 Iustin Pop
  @param ip: the address to be checked
703 58885d79 Iustin Pop
  @rtype: a regular expression match object
704 58885d79 Iustin Pop
  @return: a regular epression match object, or None if the
705 58885d79 Iustin Pop
      address is not valid
706 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1063 58885d79 Iustin Pop
  @type args: list
1064 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1065 58885d79 Iustin Pop
  @rtype: str
1066 58885d79 Iustin Pop
  @return: the quoted arguments concatenaned with spaces
1067 a8083063 Iustin Pop

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

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

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

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

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

1124 caad16e2 Iustin Pop
  @type address: string
1125 caad16e2 Iustin Pop
  @param address: the addres to check
1126 caad16e2 Iustin Pop
  @rtype: bool
1127 58885d79 Iustin Pop
  @return: True if we own the address
1128 caad16e2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1378 f7414041 Michael Hanselmann
  Element order is preserved.
1379 58885d79 Iustin Pop

1380 58885d79 Iustin Pop
  @type seq: sequence
1381 58885d79 Iustin Pop
  @param seq: the sequence with the source elementes
1382 58885d79 Iustin Pop
  @rtype: list
1383 58885d79 Iustin Pop
  @return: list of unique elements from seq
1384 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1549 b330ac0b Guido Trotter
  Any errors are ignored.
1550 b330ac0b Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1714 58885d79 Iustin Pop
  @rtype: int
1715 58885d79 Iustin Pop

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

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

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

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

1796 f65f63ef Iustin Pop
  @param fname: the file name
1797 f65f63ef Iustin Pop
  @type lines: int
1798 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1799 f65f63ef Iustin Pop

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

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

1827 26f15862 Iustin Pop
  @type text: str or unicode
1828 26f15862 Iustin Pop
  @param text: input data
1829 26f15862 Iustin Pop
  @rtype: str
1830 26f15862 Iustin Pop
  @return: a safe version of text
1831 26f15862 Iustin Pop

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

1855 835528af Iustin Pop
  @param names: set, list or tuple
1856 835528af Iustin Pop
  @return: a string with the formatted results
1857 835528af Iustin Pop

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

1865 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1866 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1867 7996a135 Iustin Pop

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

1892 58885d79 Iustin Pop
  @type fd: int
1893 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1894 58885d79 Iustin Pop

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

1907 a87b4824 Michael Hanselmann
  """
1908 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1909 58885d79 Iustin Pop
    """Constructor for FileLock.
1910 58885d79 Iustin Pop

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

1913 58885d79 Iustin Pop
    @type filename: str
1914 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1915 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

2005 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2006 58885d79 Iustin Pop
    operation::
2007 58885d79 Iustin Pop

2008 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2009 58885d79 Iustin Pop
      operations.
2010 58885d79 Iustin Pop

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

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

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

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

2036 de499029 Michael Hanselmann
  """
2037 de499029 Michael Hanselmann
  def __init__(self, signum):
2038 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2039 de499029 Michael Hanselmann

2040 58885d79 Iustin Pop
    @type signum: int or list of ints
2041 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2042 de499029 Michael Hanselmann

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

2075 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2076 58885d79 Iustin Pop

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

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

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

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

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

2108 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2109 a2d2e1a7 Iustin Pop

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

2121 a2d2e1a7 Iustin Pop
    @type field: str
2122 a2d2e1a7 Iustin Pop
    @param field: the string to match
2123 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2124 a2d2e1a7 Iustin Pop

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

2133 a2d2e1a7 Iustin Pop
    @type items: list
2134 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2135 a2d2e1a7 Iustin Pop
    @rtype: list
2136 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2137 a2d2e1a7 Iustin Pop

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