Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 256eb94b

History | View | Annotate | Download (60.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 os
31 a8083063 Iustin Pop
import time
32 113b55aa Iustin Pop
import subprocess
33 a8083063 Iustin Pop
import re
34 a8083063 Iustin Pop
import socket
35 a8083063 Iustin Pop
import tempfile
36 a8083063 Iustin Pop
import shutil
37 4ca1b175 Alexander Schreiber
import errno
38 2f8b60b3 Iustin Pop
import pwd
39 78feb6fb Guido Trotter
import itertools
40 9c233417 Iustin Pop
import select
41 9c233417 Iustin Pop
import fcntl
42 8f765069 Iustin Pop
import resource
43 bb698c1f Iustin Pop
import logging
44 de499029 Michael Hanselmann
import signal
45 256eb94b Guido Trotter
import string
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 e67bd559 Michael Hanselmann
debug_locks = False
63 58885d79 Iustin Pop
64 58885d79 Iustin Pop
#: when set to True, L{RunCmd} is disabled
65 b74159ee Iustin Pop
no_fork = False
66 f362096f Iustin Pop
67 13998ef2 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
68 13998ef2 Michael Hanselmann
69 7c0d6283 Michael Hanselmann
70 a8083063 Iustin Pop
class RunResult(object):
71 58885d79 Iustin Pop
  """Holds the result of running external programs.
72 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

470 58885d79 Iustin Pop
  @type  pidfile: string
471 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
472 58885d79 Iustin Pop
  @rtype: int
473 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
474 d9f311d7 Iustin Pop
           otherwise 0
475 fee80e90 Guido Trotter

476 fee80e90 Guido Trotter
  """
477 fee80e90 Guido Trotter
  try:
478 13998ef2 Michael Hanselmann
    raw_data = ReadFile(pidfile)
479 d9f311d7 Iustin Pop
  except EnvironmentError, err:
480 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
481 13998ef2 Michael Hanselmann
      logging.exception("Can't read pid file")
482 d9f311d7 Iustin Pop
    return 0
483 fee80e90 Guido Trotter
484 fee80e90 Guido Trotter
  try:
485 13998ef2 Michael Hanselmann
    pid = int(raw_data)
486 d9f311d7 Iustin Pop
  except ValueError, err:
487 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
488 d9f311d7 Iustin Pop
    return 0
489 fee80e90 Guido Trotter
490 d9f311d7 Iustin Pop
  return pid
491 fee80e90 Guido Trotter
492 fee80e90 Guido Trotter
493 256eb94b Guido Trotter
def MatchNameComponent(key, name_list, case_sensitive=True):
494 a8083063 Iustin Pop
  """Try to match a name against a list.
495 a8083063 Iustin Pop

496 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
497 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
498 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
499 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
500 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
501 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
502 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
503 a8083063 Iustin Pop

504 58885d79 Iustin Pop
  @type key: str
505 58885d79 Iustin Pop
  @param key: the name to be searched
506 58885d79 Iustin Pop
  @type name_list: list
507 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
508 256eb94b Guido Trotter
  @type case_sensitive: boolean
509 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
510 a8083063 Iustin Pop

511 58885d79 Iustin Pop
  @rtype: None or str
512 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
513 58885d79 Iustin Pop
      otherwise the element from the list which matches
514 a8083063 Iustin Pop

515 a8083063 Iustin Pop
  """
516 3a541d90 Iustin Pop
  if key in name_list:
517 3a541d90 Iustin Pop
    return key
518 256eb94b Guido Trotter
519 256eb94b Guido Trotter
  re_flags = 0
520 256eb94b Guido Trotter
  if not case_sensitive:
521 256eb94b Guido Trotter
    re_flags |= re.IGNORECASE
522 256eb94b Guido Trotter
    key = string.upper(key)
523 256eb94b Guido Trotter
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
524 256eb94b Guido Trotter
  names_filtered = []
525 256eb94b Guido Trotter
  string_matches = []
526 256eb94b Guido Trotter
  for name in name_list:
527 256eb94b Guido Trotter
    if mo.match(name) is not None:
528 256eb94b Guido Trotter
      names_filtered.append(name)
529 256eb94b Guido Trotter
      if not case_sensitive and key == string.upper(name):
530 256eb94b Guido Trotter
        string_matches.append(name)
531 256eb94b Guido Trotter
532 256eb94b Guido Trotter
  if len(string_matches) == 1:
533 256eb94b Guido Trotter
    return string_matches[0]
534 256eb94b Guido Trotter
  if len(names_filtered) == 1:
535 256eb94b Guido Trotter
    return names_filtered[0]
536 256eb94b Guido Trotter
  return None
537 a8083063 Iustin Pop
538 a8083063 Iustin Pop
539 bcf043c9 Iustin Pop
class HostInfo:
540 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
541 bcf043c9 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1174 59072e7e Michael Hanselmann
  """
1175 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1176 087b34fe Iustin Pop
1177 087b34fe Iustin Pop
1178 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1179 33081d90 Iustin Pop
  """Generates a random secret.
1180 33081d90 Iustin Pop

1181 ec2c2bc4 Luca Bigliardi
  This will generate a pseudo-random secret returning an hex string
1182 33081d90 Iustin Pop
  (so that it can be used where an ASCII string is needed).
1183 33081d90 Iustin Pop

1184 ec2c2bc4 Luca Bigliardi
  @param numbytes: the number of bytes which will be represented by the returned
1185 ec2c2bc4 Luca Bigliardi
      string (defaulting to 20, the length of a SHA1 hash)
1186 58885d79 Iustin Pop
  @rtype: str
1187 ec2c2bc4 Luca Bigliardi
  @return: an hex representation of the pseudo-random sequence
1188 58885d79 Iustin Pop

1189 33081d90 Iustin Pop
  """
1190 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1191 33081d90 Iustin Pop
1192 33081d90 Iustin Pop
1193 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1194 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1195 9dae41ad Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1374 f7414041 Michael Hanselmann
  Element order is preserved.
1375 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1545 b330ac0b Guido Trotter
  Any errors are ignored.
1546 b330ac0b Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1709 cd50653c Guido Trotter
  @type daemon_name: string
1710 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1711 58885d79 Iustin Pop
  @rtype: int
1712 58885d79 Iustin Pop

1713 4a8b186a Michael Hanselmann
  """
1714 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1715 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1716 cd50653c Guido Trotter
1717 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1718 4a8b186a Michael Hanselmann
  try:
1719 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1720 4a8b186a Michael Hanselmann
  except socket.error:
1721 cd50653c Guido Trotter
    port = default_port
1722 4a8b186a Michael Hanselmann
1723 4a8b186a Michael Hanselmann
  return port
1724 4a8b186a Michael Hanselmann
1725 4a8b186a Michael Hanselmann
1726 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1727 d21d09d6 Iustin Pop
                 multithreaded=False):
1728 82d9caef Iustin Pop
  """Configures the logging module.
1729 82d9caef Iustin Pop

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

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

1793 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1794 da961187 Guido Trotter

1795 da961187 Guido Trotter
  """
1796 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1797 82d9caef Iustin Pop
1798 016d04b3 Michael Hanselmann
1799 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1800 f65f63ef Iustin Pop
  """Return the last lines from a file.
1801 f65f63ef Iustin Pop

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

1806 f65f63ef Iustin Pop
  @param fname: the file name
1807 f65f63ef Iustin Pop
  @type lines: int
1808 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1809 f65f63ef Iustin Pop

1810 f65f63ef Iustin Pop
  """
1811 f65f63ef Iustin Pop
  fd = open(fname, "r")
1812 f65f63ef Iustin Pop
  try:
1813 f65f63ef Iustin Pop
    fd.seek(0, 2)
1814 f65f63ef Iustin Pop
    pos = fd.tell()
1815 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1816 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1817 f65f63ef Iustin Pop
    raw_data = fd.read()
1818 f65f63ef Iustin Pop
  finally:
1819 f65f63ef Iustin Pop
    fd.close()
1820 f65f63ef Iustin Pop
1821 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1822 f65f63ef Iustin Pop
  return rows[-lines:]
1823 f65f63ef Iustin Pop
1824 f65f63ef Iustin Pop
1825 26f15862 Iustin Pop
def SafeEncode(text):
1826 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1827 26f15862 Iustin Pop

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

1837 26f15862 Iustin Pop
  @type text: str or unicode
1838 26f15862 Iustin Pop
  @param text: input data
1839 26f15862 Iustin Pop
  @rtype: str
1840 26f15862 Iustin Pop
  @return: a safe version of text
1841 26f15862 Iustin Pop

1842 26f15862 Iustin Pop
  """
1843 d392fa34 Iustin Pop
  if isinstance(text, unicode):
1844 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
1845 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
1846 d392fa34 Iustin Pop
  resu = ""
1847 d392fa34 Iustin Pop
  for char in text:
1848 d392fa34 Iustin Pop
    c = ord(char)
1849 d392fa34 Iustin Pop
    if char  == '\t':
1850 d392fa34 Iustin Pop
      resu += r'\t'
1851 d392fa34 Iustin Pop
    elif char == '\n':
1852 d392fa34 Iustin Pop
      resu += r'\n'
1853 d392fa34 Iustin Pop
    elif char == '\r':
1854 d392fa34 Iustin Pop
      resu += r'\'r'
1855 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
1856 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
1857 d392fa34 Iustin Pop
    else:
1858 d392fa34 Iustin Pop
      resu += char
1859 d392fa34 Iustin Pop
  return resu
1860 26f15862 Iustin Pop
1861 26f15862 Iustin Pop
1862 835528af Iustin Pop
def CommaJoin(names):
1863 835528af Iustin Pop
  """Nicely join a set of identifiers.
1864 835528af Iustin Pop

1865 835528af Iustin Pop
  @param names: set, list or tuple
1866 835528af Iustin Pop
  @return: a string with the formatted results
1867 835528af Iustin Pop

1868 835528af Iustin Pop
  """
1869 835528af Iustin Pop
  return ", ".join(["'%s'" % val for val in names])
1870 835528af Iustin Pop
1871 835528af Iustin Pop
1872 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
1873 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
1874 3f6a47a8 Michael Hanselmann

1875 3f6a47a8 Michael Hanselmann
  @type value: int
1876 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
1877 3f6a47a8 Michael Hanselmann
  @rtype: int
1878 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
1879 3f6a47a8 Michael Hanselmann

1880 3f6a47a8 Michael Hanselmann
  """
1881 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
1882 3f6a47a8 Michael Hanselmann
1883 3f6a47a8 Michael Hanselmann
1884 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
1885 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
1886 3f6a47a8 Michael Hanselmann

1887 3f6a47a8 Michael Hanselmann
  @type path: string
1888 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
1889 3f6a47a8 Michael Hanselmann
  @rtype: int
1890 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
1891 3f6a47a8 Michael Hanselmann

1892 3f6a47a8 Michael Hanselmann
  """
1893 3f6a47a8 Michael Hanselmann
  size = 0
1894 3f6a47a8 Michael Hanselmann
1895 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
1896 2a887df9 Michael Hanselmann
    for filename in files:
1897 2a887df9 Michael Hanselmann
      st = os.lstat(os.path.join(curpath, filename))
1898 3f6a47a8 Michael Hanselmann
      size += st.st_size
1899 3f6a47a8 Michael Hanselmann
1900 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
1901 3f6a47a8 Michael Hanselmann
1902 3f6a47a8 Michael Hanselmann
1903 3f6a47a8 Michael Hanselmann
def GetFreeFilesystemSpace(path):
1904 3f6a47a8 Michael Hanselmann
  """Returns the free space on a filesystem.
1905 3f6a47a8 Michael Hanselmann

1906 3f6a47a8 Michael Hanselmann
  @type path: string
1907 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
1908 3f6a47a8 Michael Hanselmann
  @rtype: int
1909 3f6a47a8 Michael Hanselmann
  @return: Free space in mebibytes
1910 3f6a47a8 Michael Hanselmann

1911 3f6a47a8 Michael Hanselmann
  """
1912 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
1913 3f6a47a8 Michael Hanselmann
1914 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(st.f_bavail * st.f_frsize)
1915 3f6a47a8 Michael Hanselmann
1916 3f6a47a8 Michael Hanselmann
1917 7996a135 Iustin Pop
def LockedMethod(fn):
1918 7996a135 Iustin Pop
  """Synchronized object access decorator.
1919 7996a135 Iustin Pop

1920 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1921 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1922 7996a135 Iustin Pop

1923 7996a135 Iustin Pop
  """
1924 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1925 e67bd559 Michael Hanselmann
    if debug_locks:
1926 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1927 e67bd559 Michael Hanselmann
1928 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1929 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1930 7996a135 Iustin Pop
    lock = self._lock
1931 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1932 7996a135 Iustin Pop
    lock.acquire()
1933 7996a135 Iustin Pop
    try:
1934 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1935 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1936 7996a135 Iustin Pop
    finally:
1937 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1938 7996a135 Iustin Pop
      lock.release()
1939 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1940 7996a135 Iustin Pop
    return result
1941 7996a135 Iustin Pop
  return wrapper
1942 eb0f0ce0 Michael Hanselmann
1943 eb0f0ce0 Michael Hanselmann
1944 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1945 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1946 eb0f0ce0 Michael Hanselmann

1947 58885d79 Iustin Pop
  @type fd: int
1948 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1949 58885d79 Iustin Pop

1950 eb0f0ce0 Michael Hanselmann
  """
1951 eb0f0ce0 Michael Hanselmann
  try:
1952 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1953 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1954 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1955 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1956 eb0f0ce0 Michael Hanselmann
    raise
1957 de499029 Michael Hanselmann
1958 de499029 Michael Hanselmann
1959 3b813dd2 Iustin Pop
def FormatTime(val):
1960 3b813dd2 Iustin Pop
  """Formats a time value.
1961 3b813dd2 Iustin Pop

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

1966 3b813dd2 Iustin Pop
  """
1967 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
1968 3b813dd2 Iustin Pop
    return "N/A"
1969 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
1970 3b813dd2 Iustin Pop
  # platforms
1971 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
1972 3b813dd2 Iustin Pop
1973 3b813dd2 Iustin Pop
1974 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
1975 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
1976 05e50653 Michael Hanselmann

1977 5cbe43a5 Michael Hanselmann
  @type filename: string
1978 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
1979 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
1980 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
1981 5cbe43a5 Michael Hanselmann
  @type remove_after: int
1982 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
1983 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
1984 5cbe43a5 Michael Hanselmann

1985 05e50653 Michael Hanselmann
  """
1986 05e50653 Michael Hanselmann
  if now is None:
1987 05e50653 Michael Hanselmann
    now = time.time()
1988 05e50653 Michael Hanselmann
1989 05e50653 Michael Hanselmann
  try:
1990 05e50653 Michael Hanselmann
    value = ReadFile(filename)
1991 05e50653 Michael Hanselmann
  except IOError, err:
1992 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
1993 05e50653 Michael Hanselmann
      raise
1994 05e50653 Michael Hanselmann
    value = None
1995 05e50653 Michael Hanselmann
1996 05e50653 Michael Hanselmann
  if value is not None:
1997 05e50653 Michael Hanselmann
    try:
1998 05e50653 Michael Hanselmann
      value = int(value)
1999 05e50653 Michael Hanselmann
    except ValueError:
2000 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2001 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2002 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2003 05e50653 Michael Hanselmann
      value = None
2004 05e50653 Michael Hanselmann
2005 05e50653 Michael Hanselmann
    if value is not None:
2006 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2007 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2008 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2009 5cbe43a5 Michael Hanselmann
        value = None
2010 5cbe43a5 Michael Hanselmann
2011 5cbe43a5 Michael Hanselmann
      elif now > value:
2012 05e50653 Michael Hanselmann
        value = None
2013 05e50653 Michael Hanselmann
2014 05e50653 Michael Hanselmann
  return value
2015 05e50653 Michael Hanselmann
2016 05e50653 Michael Hanselmann
2017 a87b4824 Michael Hanselmann
class FileLock(object):
2018 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2019 a87b4824 Michael Hanselmann

2020 a87b4824 Michael Hanselmann
  """
2021 a87b4824 Michael Hanselmann
  def __init__(self, filename):
2022 58885d79 Iustin Pop
    """Constructor for FileLock.
2023 58885d79 Iustin Pop

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

2026 58885d79 Iustin Pop
    @type filename: str
2027 58885d79 Iustin Pop
    @param filename: path to the file to be locked
2028 58885d79 Iustin Pop

2029 58885d79 Iustin Pop
    """
2030 a87b4824 Michael Hanselmann
    self.filename = filename
2031 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
2032 a87b4824 Michael Hanselmann
2033 a87b4824 Michael Hanselmann
  def __del__(self):
2034 a87b4824 Michael Hanselmann
    self.Close()
2035 a87b4824 Michael Hanselmann
2036 a87b4824 Michael Hanselmann
  def Close(self):
2037 58885d79 Iustin Pop
    """Close the file and release the lock.
2038 58885d79 Iustin Pop

2039 58885d79 Iustin Pop
    """
2040 a87b4824 Michael Hanselmann
    if self.fd:
2041 a87b4824 Michael Hanselmann
      self.fd.close()
2042 a87b4824 Michael Hanselmann
      self.fd = None
2043 a87b4824 Michael Hanselmann
2044 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2045 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2046 aa74b828 Michael Hanselmann

2047 aa74b828 Michael Hanselmann
    @type flag: int
2048 58885d79 Iustin Pop
    @param flag: operation flag
2049 aa74b828 Michael Hanselmann
    @type blocking: bool
2050 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2051 aa74b828 Michael Hanselmann
    @type timeout: None or float
2052 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2053 aa74b828 Michael Hanselmann
                    non-blocking mode).
2054 aa74b828 Michael Hanselmann
    @type errmsg: string
2055 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2056 aa74b828 Michael Hanselmann

2057 aa74b828 Michael Hanselmann
    """
2058 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2059 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2060 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2061 a87b4824 Michael Hanselmann
2062 aa74b828 Michael Hanselmann
    if timeout is not None:
2063 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2064 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
2065 a87b4824 Michael Hanselmann
2066 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
2067 aa74b828 Michael Hanselmann
    elif not blocking:
2068 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2069 aa74b828 Michael Hanselmann
      timeout_end = None
2070 aa74b828 Michael Hanselmann
2071 aa74b828 Michael Hanselmann
    retry = True
2072 aa74b828 Michael Hanselmann
    while retry:
2073 aa74b828 Michael Hanselmann
      try:
2074 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
2075 aa74b828 Michael Hanselmann
        retry = False
2076 aa74b828 Michael Hanselmann
      except IOError, err:
2077 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
2078 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
2079 aa74b828 Michael Hanselmann
            # Wait before trying again
2080 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
2081 aa74b828 Michael Hanselmann
          else:
2082 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
2083 aa74b828 Michael Hanselmann
        else:
2084 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
2085 aa74b828 Michael Hanselmann
          raise
2086 aa74b828 Michael Hanselmann
2087 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2088 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2089 a87b4824 Michael Hanselmann

2090 58885d79 Iustin Pop
    @type blocking: boolean
2091 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2092 58885d79 Iustin Pop
        can lock the file or return immediately
2093 58885d79 Iustin Pop
    @type timeout: int or None
2094 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2095 58885d79 Iustin Pop
        (in blocking mode)
2096 58885d79 Iustin Pop

2097 a87b4824 Michael Hanselmann
    """
2098 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2099 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2100 a87b4824 Michael Hanselmann
2101 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2102 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2103 a87b4824 Michael Hanselmann

2104 58885d79 Iustin Pop
    @type blocking: boolean
2105 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2106 58885d79 Iustin Pop
        can lock the file or return immediately
2107 58885d79 Iustin Pop
    @type timeout: int or None
2108 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2109 58885d79 Iustin Pop
        (in blocking mode)
2110 58885d79 Iustin Pop

2111 a87b4824 Michael Hanselmann
    """
2112 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2113 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2114 a87b4824 Michael Hanselmann
2115 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2116 a87b4824 Michael Hanselmann
    """Unlocks the file.
2117 a87b4824 Michael Hanselmann

2118 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2119 58885d79 Iustin Pop
    operation::
2120 58885d79 Iustin Pop

2121 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2122 58885d79 Iustin Pop
      operations.
2123 58885d79 Iustin Pop

2124 58885d79 Iustin Pop
    @type blocking: boolean
2125 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2126 58885d79 Iustin Pop
        can lock the file or return immediately
2127 58885d79 Iustin Pop
    @type timeout: int or None
2128 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2129 58885d79 Iustin Pop
        (in blocking mode)
2130 a87b4824 Michael Hanselmann

2131 a87b4824 Michael Hanselmann
    """
2132 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2133 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2134 a87b4824 Michael Hanselmann
2135 a87b4824 Michael Hanselmann
2136 451575de Guido Trotter
def SignalHandled(signums):
2137 451575de Guido Trotter
  """Signal Handled decoration.
2138 451575de Guido Trotter

2139 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2140 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2141 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2142 451575de Guido Trotter
  objects as values.
2143 451575de Guido Trotter

2144 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2145 451575de Guido Trotter
  with different handlers.
2146 451575de Guido Trotter

2147 451575de Guido Trotter
  @type signums: list
2148 451575de Guido Trotter
  @param signums: signals to intercept
2149 451575de Guido Trotter

2150 451575de Guido Trotter
  """
2151 451575de Guido Trotter
  def wrap(fn):
2152 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2153 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2154 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2155 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2156 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2157 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2158 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2159 451575de Guido Trotter
      else:
2160 451575de Guido Trotter
        signal_handlers = {}
2161 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2162 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2163 451575de Guido Trotter
      try:
2164 451575de Guido Trotter
        for sig in signums:
2165 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2166 451575de Guido Trotter
        return fn(*args, **kwargs)
2167 451575de Guido Trotter
      finally:
2168 451575de Guido Trotter
        sighandler.Reset()
2169 451575de Guido Trotter
    return sig_function
2170 451575de Guido Trotter
  return wrap
2171 451575de Guido Trotter
2172 451575de Guido Trotter
2173 de499029 Michael Hanselmann
class SignalHandler(object):
2174 de499029 Michael Hanselmann
  """Generic signal handler class.
2175 de499029 Michael Hanselmann

2176 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2177 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2178 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2179 58885d79 Iustin Pop
  signal was sent.
2180 58885d79 Iustin Pop

2181 58885d79 Iustin Pop
  @type signum: list
2182 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2183 58885d79 Iustin Pop
  @type called: boolean
2184 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2185 de499029 Michael Hanselmann

2186 de499029 Michael Hanselmann
  """
2187 de499029 Michael Hanselmann
  def __init__(self, signum):
2188 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2189 de499029 Michael Hanselmann

2190 58885d79 Iustin Pop
    @type signum: int or list of ints
2191 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2192 de499029 Michael Hanselmann

2193 de499029 Michael Hanselmann
    """
2194 6c52849e Guido Trotter
    self.signum = set(signum)
2195 de499029 Michael Hanselmann
    self.called = False
2196 de499029 Michael Hanselmann
2197 de499029 Michael Hanselmann
    self._previous = {}
2198 de499029 Michael Hanselmann
    try:
2199 de499029 Michael Hanselmann
      for signum in self.signum:
2200 de499029 Michael Hanselmann
        # Setup handler
2201 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2202 de499029 Michael Hanselmann
        try:
2203 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2204 de499029 Michael Hanselmann
        except:
2205 de499029 Michael Hanselmann
          # Restore previous handler
2206 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2207 de499029 Michael Hanselmann
          raise
2208 de499029 Michael Hanselmann
    except:
2209 de499029 Michael Hanselmann
      # Reset all handlers
2210 de499029 Michael Hanselmann
      self.Reset()
2211 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2212 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2213 de499029 Michael Hanselmann
      raise
2214 de499029 Michael Hanselmann
2215 de499029 Michael Hanselmann
  def __del__(self):
2216 de499029 Michael Hanselmann
    self.Reset()
2217 de499029 Michael Hanselmann
2218 de499029 Michael Hanselmann
  def Reset(self):
2219 de499029 Michael Hanselmann
    """Restore previous handler.
2220 de499029 Michael Hanselmann

2221 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2222 58885d79 Iustin Pop

2223 de499029 Michael Hanselmann
    """
2224 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2225 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2226 de499029 Michael Hanselmann
      # If successful, remove from dict
2227 de499029 Michael Hanselmann
      del self._previous[signum]
2228 de499029 Michael Hanselmann
2229 de499029 Michael Hanselmann
  def Clear(self):
2230 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2231 de499029 Michael Hanselmann

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

2234 de499029 Michael Hanselmann
    """
2235 de499029 Michael Hanselmann
    self.called = False
2236 de499029 Michael Hanselmann
2237 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
2238 de499029 Michael Hanselmann
    """Actual signal handling function.
2239 de499029 Michael Hanselmann

2240 de499029 Michael Hanselmann
    """
2241 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2242 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2243 de499029 Michael Hanselmann
    self.called = True
2244 a2d2e1a7 Iustin Pop
2245 a2d2e1a7 Iustin Pop
2246 a2d2e1a7 Iustin Pop
class FieldSet(object):
2247 a2d2e1a7 Iustin Pop
  """A simple field set.
2248 a2d2e1a7 Iustin Pop

2249 a2d2e1a7 Iustin Pop
  Among the features are:
2250 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2251 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2252 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2253 a2d2e1a7 Iustin Pop

2254 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2255 a2d2e1a7 Iustin Pop

2256 a2d2e1a7 Iustin Pop
  """
2257 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2258 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2259 a2d2e1a7 Iustin Pop
2260 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2261 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2262 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2263 a2d2e1a7 Iustin Pop
2264 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2265 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2266 a2d2e1a7 Iustin Pop

2267 a2d2e1a7 Iustin Pop
    @type field: str
2268 a2d2e1a7 Iustin Pop
    @param field: the string to match
2269 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2270 a2d2e1a7 Iustin Pop

2271 a2d2e1a7 Iustin Pop
    """
2272 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2273 a2d2e1a7 Iustin Pop
      return m
2274 a2d2e1a7 Iustin Pop
    return False
2275 a2d2e1a7 Iustin Pop
2276 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2277 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2278 a2d2e1a7 Iustin Pop

2279 a2d2e1a7 Iustin Pop
    @type items: list
2280 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2281 a2d2e1a7 Iustin Pop
    @rtype: list
2282 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2283 a2d2e1a7 Iustin Pop

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