Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ f9897b6d

History | View | Annotate | Download (60 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 d6f5892b Michael Hanselmann
import imp
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 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
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 58885d79 Iustin Pop
  'test1.example.org']}).
502 a8083063 Iustin Pop

503 58885d79 Iustin Pop
  @type key: str
504 58885d79 Iustin Pop
  @param key: the name to be searched
505 58885d79 Iustin Pop
  @type name_list: list
506 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
507 a8083063 Iustin Pop

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

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

523 bcf043c9 Iustin Pop
  """
524 89e1fc26 Iustin Pop
  def __init__(self, name=None):
525 bcf043c9 Iustin Pop
    """Initialize the host name object.
526 bcf043c9 Iustin Pop

527 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
528 89e1fc26 Iustin Pop
    name.
529 bcf043c9 Iustin Pop

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

541 c8a0948f Michael Hanselmann
    """
542 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
543 c8a0948f Michael Hanselmann
544 89e1fc26 Iustin Pop
  @staticmethod
545 89e1fc26 Iustin Pop
  def SysName():
546 89e1fc26 Iustin Pop
    """Return the current system's name.
547 bcf043c9 Iustin Pop

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

550 89e1fc26 Iustin Pop
    """
551 89e1fc26 Iustin Pop
    return socket.gethostname()
552 a8083063 Iustin Pop
553 89e1fc26 Iustin Pop
  @staticmethod
554 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
555 89e1fc26 Iustin Pop
    """Look up hostname
556 a8083063 Iustin Pop

557 58885d79 Iustin Pop
    @type hostname: str
558 58885d79 Iustin Pop
    @param hostname: hostname to look up
559 89e1fc26 Iustin Pop

560 58885d79 Iustin Pop
    @rtype: tuple
561 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
562 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
563 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
564 89e1fc26 Iustin Pop

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

578 58885d79 Iustin Pop
  @rtype: dict
579 58885d79 Iustin Pop
  @return:
580 58885d79 Iustin Pop
       Dictionary with keys volume name and values
581 58885d79 Iustin Pop
       the size of the volume
582 a8083063 Iustin Pop

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

606 58885d79 Iustin Pop
  @type bridge: str
607 58885d79 Iustin Pop
  @param bridge: the bridge name to check
608 58885d79 Iustin Pop
  @rtype: boolean
609 58885d79 Iustin Pop
  @return: True if it does
610 a8083063 Iustin Pop

611 a8083063 Iustin Pop
  """
612 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
613 a8083063 Iustin Pop
614 a8083063 Iustin Pop
615 a8083063 Iustin Pop
def NiceSort(name_list):
616 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
617 a8083063 Iustin Pop

618 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
619 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
620 58885d79 Iustin Pop
  'a11']}.
621 a8083063 Iustin Pop

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

626 58885d79 Iustin Pop
  @type name_list: list
627 58885d79 Iustin Pop
  @param name_list: the names to be sorted
628 58885d79 Iustin Pop
  @rtype: list
629 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
630 a8083063 Iustin Pop

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

655 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
656 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
657 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
658 58885d79 Iustin Pop
  exceptions are propagated to the caller.
659 58885d79 Iustin Pop

660 58885d79 Iustin Pop
  @type fn: callable
661 58885d79 Iustin Pop
  @param fn: function to apply to the value
662 58885d79 Iustin Pop
  @param val: the value to be converted
663 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
664 58885d79 Iustin Pop
      otherwise the original value.
665 a8083063 Iustin Pop

666 a8083063 Iustin Pop
  """
667 a8083063 Iustin Pop
  try:
668 a8083063 Iustin Pop
    nv = fn(val)
669 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
670 a8083063 Iustin Pop
    nv = val
671 a8083063 Iustin Pop
  return nv
672 a8083063 Iustin Pop
673 a8083063 Iustin Pop
674 a8083063 Iustin Pop
def IsValidIP(ip):
675 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
676 a8083063 Iustin Pop

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

680 58885d79 Iustin Pop
  @type ip: str
681 58885d79 Iustin Pop
  @param ip: the address to be checked
682 58885d79 Iustin Pop
  @rtype: a regular expression match object
683 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
684 58885d79 Iustin Pop
      address is not valid
685 a8083063 Iustin Pop

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

695 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
696 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
697 a8083063 Iustin Pop
  the actual command.
698 a8083063 Iustin Pop

699 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
700 a8083063 Iustin Pop
  side.
701 a8083063 Iustin Pop

702 58885d79 Iustin Pop
  @type word: str
703 58885d79 Iustin Pop
  @param word: the word to check
704 58885d79 Iustin Pop
  @rtype: boolean
705 58885d79 Iustin Pop
  @return: True if the word is 'safe'
706 58885d79 Iustin Pop

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

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

719 58885d79 Iustin Pop
  @type template: str
720 58885d79 Iustin Pop
  @param template: the string holding the template for the
721 58885d79 Iustin Pop
      string formatting
722 58885d79 Iustin Pop
  @rtype: str
723 58885d79 Iustin Pop
  @return: the expanded command line
724 58885d79 Iustin Pop

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

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

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

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

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

817 58885d79 Iustin Pop
  @type file_name: str
818 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
819 58885d79 Iustin Pop
  @type key: str
820 58885d79 Iustin Pop
  @param key: string containing key
821 58885d79 Iustin Pop

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

846 58885d79 Iustin Pop
  @type file_name: str
847 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
848 58885d79 Iustin Pop
  @type key: str
849 58885d79 Iustin Pop
  @param key: string containing key
850 58885d79 Iustin Pop

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

879 58885d79 Iustin Pop
  @type file_name: str
880 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
881 58885d79 Iustin Pop
  @type ip: str
882 58885d79 Iustin Pop
  @param ip: the IP address
883 58885d79 Iustin Pop
  @type hostname: str
884 58885d79 Iustin Pop
  @param hostname: the hostname to be added
885 58885d79 Iustin Pop
  @type aliases: list
886 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
887 58885d79 Iustin Pop

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

926 58885d79 Iustin Pop
  @type hostname: str
927 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
928 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
929 58885d79 Iustin Pop

930 d9c02ca6 Michael Hanselmann
  """
931 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
932 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
933 d9c02ca6 Michael Hanselmann
934 d9c02ca6 Michael Hanselmann
935 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
936 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
937 899d2a81 Michael Hanselmann

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

940 58885d79 Iustin Pop
  @type file_name: str
941 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
942 58885d79 Iustin Pop
  @type hostname: str
943 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
944 58885d79 Iustin Pop

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

982 58885d79 Iustin Pop
  @type hostname: str
983 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
984 58885d79 Iustin Pop
      full and shot name will be removed from
985 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
986 58885d79 Iustin Pop

987 d9c02ca6 Michael Hanselmann
  """
988 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
989 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
990 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
991 d9c02ca6 Michael Hanselmann
992 d9c02ca6 Michael Hanselmann
993 a8083063 Iustin Pop
def CreateBackup(file_name):
994 a8083063 Iustin Pop
  """Creates a backup of a file.
995 a8083063 Iustin Pop

996 58885d79 Iustin Pop
  @type file_name: str
997 58885d79 Iustin Pop
  @param file_name: file to be backed up
998 58885d79 Iustin Pop
  @rtype: str
999 58885d79 Iustin Pop
  @return: the path to the newly created backup
1000 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1001 a8083063 Iustin Pop

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

1027 58885d79 Iustin Pop
  @type value: str
1028 58885d79 Iustin Pop
  @param value: the argument to be quoted
1029 58885d79 Iustin Pop
  @rtype: str
1030 58885d79 Iustin Pop
  @return: the quoted value
1031 58885d79 Iustin Pop

1032 a8083063 Iustin Pop
  """
1033 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1034 a8083063 Iustin Pop
    return value
1035 a8083063 Iustin Pop
  else:
1036 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1037 a8083063 Iustin Pop
1038 a8083063 Iustin Pop
1039 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1040 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1041 58885d79 Iustin Pop

1042 58885d79 Iustin Pop
  @type args: list
1043 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1044 58885d79 Iustin Pop
  @rtype: str
1045 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1046 a8083063 Iustin Pop

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

1054 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1055 58885d79 Iustin Pop
  to it.
1056 58885d79 Iustin Pop

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

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

1100 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1101 caad16e2 Iustin Pop
  address.
1102 caad16e2 Iustin Pop

1103 caad16e2 Iustin Pop
  @type address: string
1104 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1105 caad16e2 Iustin Pop
  @rtype: bool
1106 58885d79 Iustin Pop
  @return: True if we own the address
1107 caad16e2 Iustin Pop

1108 caad16e2 Iustin Pop
  """
1109 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1110 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1111 caad16e2 Iustin Pop
1112 caad16e2 Iustin Pop
1113 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1114 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1115 58885d79 Iustin Pop

1116 58885d79 Iustin Pop
  @type path: str
1117 58885d79 Iustin Pop
  @param path: the directory to enumerate
1118 58885d79 Iustin Pop
  @rtype: list
1119 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1120 eedbda4b Michael Hanselmann

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

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

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

1151 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1152 58885d79 Iustin Pop
      filesystem.
1153 58885d79 Iustin Pop
  @rtype: str
1154 58885d79 Iustin Pop

1155 59072e7e Michael Hanselmann
  """
1156 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1157 087b34fe Iustin Pop
1158 087b34fe Iustin Pop
1159 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1160 33081d90 Iustin Pop
  """Generates a random secret.
1161 33081d90 Iustin Pop

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

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

1170 33081d90 Iustin Pop
  """
1171 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1172 33081d90 Iustin Pop
1173 33081d90 Iustin Pop
1174 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1175 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1176 9dae41ad Guido Trotter

1177 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1178 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1179 9dae41ad Guido Trotter

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

1195 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1196 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1197 58885d79 Iustin Pop
  @rtype: str
1198 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1199 ca0aa6d0 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

1355 f7414041 Michael Hanselmann
  Element order is preserved.
1356 58885d79 Iustin Pop

1357 58885d79 Iustin Pop
  @type seq: sequence
1358 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1359 58885d79 Iustin Pop
  @rtype: list
1360 58885d79 Iustin Pop
  @return: list of unique elements from seq
1361 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

1421 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1422 7d88772a Iustin Pop
  stdin/out/err).
1423 8f765069 Iustin Pop

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

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

1453 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1454 7d88772a Iustin Pop
  runs it in the background as a daemon.
1455 7d88772a Iustin Pop

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

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

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

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

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

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

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

1526 b330ac0b Guido Trotter
  Any errors are ignored.
1527 b330ac0b Guido Trotter

1528 58885d79 Iustin Pop
  @type name: str
1529 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1530 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1690 cd50653c Guido Trotter
  @type daemon_name: string
1691 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1692 58885d79 Iustin Pop
  @rtype: int
1693 58885d79 Iustin Pop

1694 4a8b186a Michael Hanselmann
  """
1695 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1696 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1697 cd50653c Guido Trotter
1698 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1699 4a8b186a Michael Hanselmann
  try:
1700 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1701 4a8b186a Michael Hanselmann
  except socket.error:
1702 cd50653c Guido Trotter
    port = default_port
1703 4a8b186a Michael Hanselmann
1704 4a8b186a Michael Hanselmann
  return port
1705 4a8b186a Michael Hanselmann
1706 4a8b186a Michael Hanselmann
1707 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1708 d21d09d6 Iustin Pop
                 multithreaded=False):
1709 82d9caef Iustin Pop
  """Configures the logging module.
1710 82d9caef Iustin Pop

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

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

1774 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1775 da961187 Guido Trotter

1776 da961187 Guido Trotter
  """
1777 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1778 82d9caef Iustin Pop
1779 016d04b3 Michael Hanselmann
1780 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1781 f65f63ef Iustin Pop
  """Return the last lines from a file.
1782 f65f63ef Iustin Pop

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

1787 f65f63ef Iustin Pop
  @param fname: the file name
1788 f65f63ef Iustin Pop
  @type lines: int
1789 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1790 f65f63ef Iustin Pop

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

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

1818 26f15862 Iustin Pop
  @type text: str or unicode
1819 26f15862 Iustin Pop
  @param text: input data
1820 26f15862 Iustin Pop
  @rtype: str
1821 26f15862 Iustin Pop
  @return: a safe version of text
1822 26f15862 Iustin Pop

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

1846 835528af Iustin Pop
  @param names: set, list or tuple
1847 835528af Iustin Pop
  @return: a string with the formatted results
1848 835528af Iustin Pop

1849 835528af Iustin Pop
  """
1850 835528af Iustin Pop
  return ", ".join(["'%s'" % val for val in names])
1851 835528af Iustin Pop
1852 835528af Iustin Pop
1853 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
1854 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
1855 3f6a47a8 Michael Hanselmann

1856 3f6a47a8 Michael Hanselmann
  @type value: int
1857 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
1858 3f6a47a8 Michael Hanselmann
  @rtype: int
1859 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
1860 3f6a47a8 Michael Hanselmann

1861 3f6a47a8 Michael Hanselmann
  """
1862 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
1863 3f6a47a8 Michael Hanselmann
1864 3f6a47a8 Michael Hanselmann
1865 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
1866 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
1867 3f6a47a8 Michael Hanselmann

1868 3f6a47a8 Michael Hanselmann
  @type path: string
1869 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
1870 3f6a47a8 Michael Hanselmann
  @rtype: int
1871 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
1872 3f6a47a8 Michael Hanselmann

1873 3f6a47a8 Michael Hanselmann
  """
1874 3f6a47a8 Michael Hanselmann
  size = 0
1875 3f6a47a8 Michael Hanselmann
1876 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
1877 2a887df9 Michael Hanselmann
    for filename in files:
1878 2a887df9 Michael Hanselmann
      st = os.lstat(os.path.join(curpath, filename))
1879 3f6a47a8 Michael Hanselmann
      size += st.st_size
1880 3f6a47a8 Michael Hanselmann
1881 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
1882 3f6a47a8 Michael Hanselmann
1883 3f6a47a8 Michael Hanselmann
1884 3f6a47a8 Michael Hanselmann
def GetFreeFilesystemSpace(path):
1885 3f6a47a8 Michael Hanselmann
  """Returns the free space on a filesystem.
1886 3f6a47a8 Michael Hanselmann

1887 3f6a47a8 Michael Hanselmann
  @type path: string
1888 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
1889 3f6a47a8 Michael Hanselmann
  @rtype: int
1890 3f6a47a8 Michael Hanselmann
  @return: Free space in mebibytes
1891 3f6a47a8 Michael Hanselmann

1892 3f6a47a8 Michael Hanselmann
  """
1893 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
1894 3f6a47a8 Michael Hanselmann
1895 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(st.f_bavail * st.f_frsize)
1896 3f6a47a8 Michael Hanselmann
1897 3f6a47a8 Michael Hanselmann
1898 7996a135 Iustin Pop
def LockedMethod(fn):
1899 7996a135 Iustin Pop
  """Synchronized object access decorator.
1900 7996a135 Iustin Pop

1901 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1902 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1903 7996a135 Iustin Pop

1904 7996a135 Iustin Pop
  """
1905 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1906 e67bd559 Michael Hanselmann
    if debug_locks:
1907 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1908 e67bd559 Michael Hanselmann
1909 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1910 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1911 7996a135 Iustin Pop
    lock = self._lock
1912 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1913 7996a135 Iustin Pop
    lock.acquire()
1914 7996a135 Iustin Pop
    try:
1915 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1916 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1917 7996a135 Iustin Pop
    finally:
1918 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1919 7996a135 Iustin Pop
      lock.release()
1920 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1921 7996a135 Iustin Pop
    return result
1922 7996a135 Iustin Pop
  return wrapper
1923 eb0f0ce0 Michael Hanselmann
1924 eb0f0ce0 Michael Hanselmann
1925 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1926 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1927 eb0f0ce0 Michael Hanselmann

1928 58885d79 Iustin Pop
  @type fd: int
1929 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1930 58885d79 Iustin Pop

1931 eb0f0ce0 Michael Hanselmann
  """
1932 eb0f0ce0 Michael Hanselmann
  try:
1933 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1934 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1935 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1936 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1937 eb0f0ce0 Michael Hanselmann
    raise
1938 de499029 Michael Hanselmann
1939 de499029 Michael Hanselmann
1940 3b813dd2 Iustin Pop
def FormatTime(val):
1941 3b813dd2 Iustin Pop
  """Formats a time value.
1942 3b813dd2 Iustin Pop

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

1947 3b813dd2 Iustin Pop
  """
1948 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
1949 3b813dd2 Iustin Pop
    return "N/A"
1950 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
1951 3b813dd2 Iustin Pop
  # platforms
1952 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
1953 3b813dd2 Iustin Pop
1954 3b813dd2 Iustin Pop
1955 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
1956 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
1957 05e50653 Michael Hanselmann

1958 5cbe43a5 Michael Hanselmann
  @type filename: string
1959 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
1960 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
1961 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
1962 5cbe43a5 Michael Hanselmann
  @type remove_after: int
1963 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
1964 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
1965 5cbe43a5 Michael Hanselmann

1966 05e50653 Michael Hanselmann
  """
1967 05e50653 Michael Hanselmann
  if now is None:
1968 05e50653 Michael Hanselmann
    now = time.time()
1969 05e50653 Michael Hanselmann
1970 05e50653 Michael Hanselmann
  try:
1971 05e50653 Michael Hanselmann
    value = ReadFile(filename)
1972 05e50653 Michael Hanselmann
  except IOError, err:
1973 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
1974 05e50653 Michael Hanselmann
      raise
1975 05e50653 Michael Hanselmann
    value = None
1976 05e50653 Michael Hanselmann
1977 05e50653 Michael Hanselmann
  if value is not None:
1978 05e50653 Michael Hanselmann
    try:
1979 05e50653 Michael Hanselmann
      value = int(value)
1980 05e50653 Michael Hanselmann
    except ValueError:
1981 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
1982 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
1983 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
1984 05e50653 Michael Hanselmann
      value = None
1985 05e50653 Michael Hanselmann
1986 05e50653 Michael Hanselmann
    if value is not None:
1987 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
1988 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
1989 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
1990 5cbe43a5 Michael Hanselmann
        value = None
1991 5cbe43a5 Michael Hanselmann
1992 5cbe43a5 Michael Hanselmann
      elif now > value:
1993 05e50653 Michael Hanselmann
        value = None
1994 05e50653 Michael Hanselmann
1995 05e50653 Michael Hanselmann
  return value
1996 05e50653 Michael Hanselmann
1997 05e50653 Michael Hanselmann
1998 d6f5892b Michael Hanselmann
def LoadModule(filename):
1999 d6f5892b Michael Hanselmann
  """Loads an external module by filename.
2000 d6f5892b Michael Hanselmann

2001 d6f5892b Michael Hanselmann
  Use this function with caution. Python will always write the compiled source
2002 d6f5892b Michael Hanselmann
  to a file named "${filename}c".
2003 d6f5892b Michael Hanselmann

2004 d6f5892b Michael Hanselmann
  @type filename: string
2005 d6f5892b Michael Hanselmann
  @param filename: Path to module
2006 d6f5892b Michael Hanselmann

2007 d6f5892b Michael Hanselmann
  """
2008 d6f5892b Michael Hanselmann
  (name, ext) = os.path.splitext(filename)
2009 d6f5892b Michael Hanselmann
2010 d6f5892b Michael Hanselmann
  fh = open(filename, "U")
2011 d6f5892b Michael Hanselmann
  try:
2012 d6f5892b Michael Hanselmann
    return imp.load_module(name, fh, filename, (ext, "U", imp.PY_SOURCE))
2013 d6f5892b Michael Hanselmann
  finally:
2014 d6f5892b Michael Hanselmann
    fh.close()
2015 d6f5892b Michael Hanselmann
2016 d6f5892b 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)]