Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 9dae41ad

History | View | Annotate | Download (55.1 kB)

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

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

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

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

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

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

118 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
119 a8083063 Iustin Pop
  closed.
120 a8083063 Iustin Pop

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

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

173 36117c2b Iustin Pop
  @type  cmd: string or list
174 36117c2b Iustin Pop
  @param cmd: Command to run
175 36117c2b Iustin Pop
  @type env: dict
176 36117c2b Iustin Pop
  @param env: The environment to use
177 36117c2b Iustin Pop
  @type via_shell: bool
178 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
179 8797df43 Iustin Pop
  @type cwd: string
180 8797df43 Iustin Pop
  @param cwd: the working directory for the program
181 36117c2b Iustin Pop
  @rtype: tuple
182 36117c2b Iustin Pop
  @return: (out, err, status)
183 36117c2b Iustin Pop

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

242 36117c2b Iustin Pop
  @type  cmd: string or list
243 36117c2b Iustin Pop
  @param cmd: Command to run
244 36117c2b Iustin Pop
  @type env: dict
245 36117c2b Iustin Pop
  @param env: The environment to use
246 36117c2b Iustin Pop
  @type via_shell: bool
247 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
248 36117c2b Iustin Pop
  @type output: str
249 36117c2b Iustin Pop
  @param output: the filename in which to save the output
250 8797df43 Iustin Pop
  @type cwd: string
251 8797df43 Iustin Pop
  @param cwd: the working directory for the program
252 36117c2b Iustin Pop
  @rtype: int
253 36117c2b Iustin Pop
  @return: the exit status
254 36117c2b Iustin Pop

255 36117c2b Iustin Pop
  """
256 36117c2b Iustin Pop
  fh = open(output, "a")
257 36117c2b Iustin Pop
  try:
258 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
259 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
260 36117c2b Iustin Pop
                             stdout=fh,
261 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
262 8797df43 Iustin Pop
                             close_fds=True, env=env,
263 8797df43 Iustin Pop
                             cwd=cwd)
264 36117c2b Iustin Pop
265 36117c2b Iustin Pop
    child.stdin.close()
266 36117c2b Iustin Pop
    status = child.wait()
267 36117c2b Iustin Pop
  finally:
268 36117c2b Iustin Pop
    fh.close()
269 36117c2b Iustin Pop
  return status
270 a8083063 Iustin Pop
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
def RemoveFile(filename):
273 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
276 a8083063 Iustin Pop
  errors are passed.
277 a8083063 Iustin Pop

278 58885d79 Iustin Pop
  @type filename: str
279 58885d79 Iustin Pop
  @param filename: the file to be removed
280 58885d79 Iustin Pop

281 a8083063 Iustin Pop
  """
282 a8083063 Iustin Pop
  try:
283 a8083063 Iustin Pop
    os.unlink(filename)
284 a8083063 Iustin Pop
  except OSError, err:
285 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
286 a8083063 Iustin Pop
      raise
287 a8083063 Iustin Pop
288 a8083063 Iustin Pop
289 6e797216 Michael Hanselmann
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
290 6e797216 Michael Hanselmann
  """Renames a file.
291 6e797216 Michael Hanselmann

292 6e797216 Michael Hanselmann
  @type old: string
293 6e797216 Michael Hanselmann
  @param old: Original path
294 6e797216 Michael Hanselmann
  @type new: string
295 6e797216 Michael Hanselmann
  @param new: New path
296 6e797216 Michael Hanselmann
  @type mkdir: bool
297 6e797216 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
298 6e797216 Michael Hanselmann
  @type mkdir_mode: int
299 6e797216 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
300 6e797216 Michael Hanselmann

301 6e797216 Michael Hanselmann
  """
302 6e797216 Michael Hanselmann
  try:
303 6e797216 Michael Hanselmann
    return os.rename(old, new)
304 6e797216 Michael Hanselmann
  except OSError, err:
305 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
306 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
307 6e797216 Michael Hanselmann
    # as efficient.
308 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
309 6e797216 Michael Hanselmann
      # Create directory and try again
310 6e797216 Michael Hanselmann
      os.makedirs(os.path.dirname(new), mkdir_mode)
311 6e797216 Michael Hanselmann
      return os.rename(old, new)
312 6e797216 Michael Hanselmann
    raise
313 6e797216 Michael Hanselmann
314 6e797216 Michael Hanselmann
315 a8083063 Iustin Pop
def _FingerprintFile(filename):
316 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
317 a8083063 Iustin Pop

318 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
319 a8083063 Iustin Pop
  instead.
320 a8083063 Iustin Pop

321 58885d79 Iustin Pop
  @type filename: str
322 58885d79 Iustin Pop
  @param filename: the filename to checksum
323 58885d79 Iustin Pop
  @rtype: str
324 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
325 58885d79 Iustin Pop
      of the file
326 a8083063 Iustin Pop

327 a8083063 Iustin Pop
  """
328 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
329 a8083063 Iustin Pop
    return None
330 a8083063 Iustin Pop
331 a8083063 Iustin Pop
  f = open(filename)
332 a8083063 Iustin Pop
333 a8083063 Iustin Pop
  fp = sha.sha()
334 a8083063 Iustin Pop
  while True:
335 a8083063 Iustin Pop
    data = f.read(4096)
336 a8083063 Iustin Pop
    if not data:
337 a8083063 Iustin Pop
      break
338 a8083063 Iustin Pop
339 a8083063 Iustin Pop
    fp.update(data)
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
  return fp.hexdigest()
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
def FingerprintFiles(files):
345 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
346 a8083063 Iustin Pop

347 58885d79 Iustin Pop
  @type files: list
348 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
349 58885d79 Iustin Pop
  @rtype: dict
350 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
351 58885d79 Iustin Pop
      existing files
352 a8083063 Iustin Pop

353 a8083063 Iustin Pop
  """
354 a8083063 Iustin Pop
  ret = {}
355 a8083063 Iustin Pop
356 a8083063 Iustin Pop
  for filename in files:
357 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
358 a8083063 Iustin Pop
    if cksum:
359 a8083063 Iustin Pop
      ret[filename] = cksum
360 a8083063 Iustin Pop
361 a8083063 Iustin Pop
  return ret
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
365 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
366 a8083063 Iustin Pop

367 58885d79 Iustin Pop
  For the given dictionaries I{target} and I{template}, ensure
368 58885d79 Iustin Pop
  I{target} has all the keys from I{template}. Missing keys are added
369 58885d79 Iustin Pop
  with values from template.
370 a8083063 Iustin Pop

371 58885d79 Iustin Pop
  @type target: dict
372 58885d79 Iustin Pop
  @param target: the dictionary to update
373 58885d79 Iustin Pop
  @type template: dict
374 58885d79 Iustin Pop
  @param template: the dictionary holding the default values
375 58885d79 Iustin Pop
  @type logname: str or None
376 58885d79 Iustin Pop
  @param logname: if not None, causes the missing keys to be
377 58885d79 Iustin Pop
      logged with this name
378 a8083063 Iustin Pop

379 a8083063 Iustin Pop
  """
380 a8083063 Iustin Pop
  missing = []
381 a8083063 Iustin Pop
  for k in template:
382 a8083063 Iustin Pop
    if k not in target:
383 a8083063 Iustin Pop
      missing.append(k)
384 a8083063 Iustin Pop
      target[k] = template[k]
385 a8083063 Iustin Pop
386 a8083063 Iustin Pop
  if missing and logname:
387 bb698c1f Iustin Pop
    logging.warning('%s missing keys %s', logname, ', '.join(missing))
388 a8083063 Iustin Pop
389 a8083063 Iustin Pop
390 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
391 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
392 a5728081 Guido Trotter

393 a5728081 Guido Trotter
  @type target: dict
394 a5728081 Guido Trotter
  @param target: the dict to update
395 a5728081 Guido Trotter
  @type key_types: dict
396 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
397 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
398 a5728081 Guido Trotter
  @type allowed_values: list
399 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
400 a5728081 Guido Trotter

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

456 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
457 44bf25ff Iustin Pop
      will be returned as alive
458 58885d79 Iustin Pop
  @type pid: int
459 58885d79 Iustin Pop
  @param pid: the process ID to check
460 58885d79 Iustin Pop
  @rtype: boolean
461 58885d79 Iustin Pop
  @return: True if the process exists
462 a8083063 Iustin Pop

463 a8083063 Iustin Pop
  """
464 d9f311d7 Iustin Pop
  if pid <= 0:
465 d9f311d7 Iustin Pop
    return False
466 d9f311d7 Iustin Pop
467 a8083063 Iustin Pop
  try:
468 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
469 44bf25ff Iustin Pop
    return True
470 44bf25ff Iustin Pop
  except EnvironmentError, err:
471 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
472 a8083063 Iustin Pop
      return False
473 44bf25ff Iustin Pop
    raise
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
476 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
477 58885d79 Iustin Pop
  """Read a pid from a file.
478 fee80e90 Guido Trotter

479 58885d79 Iustin Pop
  @type  pidfile: string
480 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
481 58885d79 Iustin Pop
  @rtype: int
482 1de62f37 Guido Trotter
  @return: The process id, if the file exists and contains a valid PID,
483 d9f311d7 Iustin Pop
           otherwise 0
484 fee80e90 Guido Trotter

485 fee80e90 Guido Trotter
  """
486 fee80e90 Guido Trotter
  try:
487 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
488 d9f311d7 Iustin Pop
  except EnvironmentError, err:
489 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
490 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
491 d9f311d7 Iustin Pop
    return 0
492 fee80e90 Guido Trotter
493 fee80e90 Guido Trotter
  try:
494 fee80e90 Guido Trotter
    pid = int(pf.read())
495 d9f311d7 Iustin Pop
  except ValueError, err:
496 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
497 d9f311d7 Iustin Pop
    return 0
498 fee80e90 Guido Trotter
499 d9f311d7 Iustin Pop
  return pid
500 fee80e90 Guido Trotter
501 fee80e90 Guido Trotter
502 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
503 a8083063 Iustin Pop
  """Try to match a name against a list.
504 a8083063 Iustin Pop

505 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
506 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
507 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
508 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
509 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
510 58885d79 Iustin Pop
  'test1.example.org']}).
511 a8083063 Iustin Pop

512 58885d79 Iustin Pop
  @type key: str
513 58885d79 Iustin Pop
  @param key: the name to be searched
514 58885d79 Iustin Pop
  @type name_list: list
515 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
516 a8083063 Iustin Pop

517 58885d79 Iustin Pop
  @rtype: None or str
518 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
519 58885d79 Iustin Pop
      otherwise the element from the list which matches
520 a8083063 Iustin Pop

521 a8083063 Iustin Pop
  """
522 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
523 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
524 a8083063 Iustin Pop
  if len(names_filtered) != 1:
525 a8083063 Iustin Pop
    return None
526 a8083063 Iustin Pop
  return names_filtered[0]
527 a8083063 Iustin Pop
528 a8083063 Iustin Pop
529 bcf043c9 Iustin Pop
class HostInfo:
530 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
531 bcf043c9 Iustin Pop

532 bcf043c9 Iustin Pop
  """
533 89e1fc26 Iustin Pop
  def __init__(self, name=None):
534 bcf043c9 Iustin Pop
    """Initialize the host name object.
535 bcf043c9 Iustin Pop

536 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
537 89e1fc26 Iustin Pop
    name.
538 bcf043c9 Iustin Pop

539 bcf043c9 Iustin Pop
    """
540 89e1fc26 Iustin Pop
    if name is None:
541 89e1fc26 Iustin Pop
      name = self.SysName()
542 89e1fc26 Iustin Pop
543 89e1fc26 Iustin Pop
    self.query = name
544 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
545 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
546 bcf043c9 Iustin Pop
547 c8a0948f Michael Hanselmann
  def ShortName(self):
548 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
549 c8a0948f Michael Hanselmann

550 c8a0948f Michael Hanselmann
    """
551 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
552 c8a0948f Michael Hanselmann
553 89e1fc26 Iustin Pop
  @staticmethod
554 89e1fc26 Iustin Pop
  def SysName():
555 89e1fc26 Iustin Pop
    """Return the current system's name.
556 bcf043c9 Iustin Pop

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

559 89e1fc26 Iustin Pop
    """
560 89e1fc26 Iustin Pop
    return socket.gethostname()
561 a8083063 Iustin Pop
562 89e1fc26 Iustin Pop
  @staticmethod
563 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
564 89e1fc26 Iustin Pop
    """Look up hostname
565 a8083063 Iustin Pop

566 58885d79 Iustin Pop
    @type hostname: str
567 58885d79 Iustin Pop
    @param hostname: hostname to look up
568 89e1fc26 Iustin Pop

569 58885d79 Iustin Pop
    @rtype: tuple
570 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
571 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
572 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
573 89e1fc26 Iustin Pop

574 89e1fc26 Iustin Pop
    """
575 89e1fc26 Iustin Pop
    try:
576 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
577 89e1fc26 Iustin Pop
    except socket.gaierror, err:
578 89e1fc26 Iustin Pop
      # hostname not found in DNS
579 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
580 a8083063 Iustin Pop
581 89e1fc26 Iustin Pop
    return result
582 a8083063 Iustin Pop
583 a8083063 Iustin Pop
584 a8083063 Iustin Pop
def ListVolumeGroups():
585 a8083063 Iustin Pop
  """List volume groups and their size
586 a8083063 Iustin Pop

587 58885d79 Iustin Pop
  @rtype: dict
588 58885d79 Iustin Pop
  @return:
589 58885d79 Iustin Pop
       Dictionary with keys volume name and values
590 58885d79 Iustin Pop
       the size of the volume
591 a8083063 Iustin Pop

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

615 58885d79 Iustin Pop
  @type bridge: str
616 58885d79 Iustin Pop
  @param bridge: the bridge name to check
617 58885d79 Iustin Pop
  @rtype: boolean
618 58885d79 Iustin Pop
  @return: True if it does
619 a8083063 Iustin Pop

620 a8083063 Iustin Pop
  """
621 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
622 a8083063 Iustin Pop
623 a8083063 Iustin Pop
624 a8083063 Iustin Pop
def NiceSort(name_list):
625 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
626 a8083063 Iustin Pop

627 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
628 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
629 58885d79 Iustin Pop
  'a11']}.
630 a8083063 Iustin Pop

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

635 58885d79 Iustin Pop
  @type name_list: list
636 58885d79 Iustin Pop
  @param name_list: the names to be sorted
637 58885d79 Iustin Pop
  @rtype: list
638 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
639 a8083063 Iustin Pop

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

664 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
665 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
666 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
667 58885d79 Iustin Pop
  exceptions are propagated to the caller.
668 58885d79 Iustin Pop

669 58885d79 Iustin Pop
  @type fn: callable
670 58885d79 Iustin Pop
  @param fn: function to apply to the value
671 58885d79 Iustin Pop
  @param val: the value to be converted
672 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
673 58885d79 Iustin Pop
      otherwise the original value.
674 a8083063 Iustin Pop

675 a8083063 Iustin Pop
  """
676 a8083063 Iustin Pop
  try:
677 a8083063 Iustin Pop
    nv = fn(val)
678 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
679 a8083063 Iustin Pop
    nv = val
680 a8083063 Iustin Pop
  return nv
681 a8083063 Iustin Pop
682 a8083063 Iustin Pop
683 a8083063 Iustin Pop
def IsValidIP(ip):
684 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
685 a8083063 Iustin Pop

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

689 58885d79 Iustin Pop
  @type ip: str
690 58885d79 Iustin Pop
  @param ip: the address to be checked
691 58885d79 Iustin Pop
  @rtype: a regular expression match object
692 58885d79 Iustin Pop
  @return: a regular epression match object, or None if the
693 58885d79 Iustin Pop
      address is not valid
694 a8083063 Iustin Pop

695 a8083063 Iustin Pop
  """
696 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
697 58885d79 Iustin Pop
  #TODO: convert and return only boolean
698 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
699 a8083063 Iustin Pop
700 a8083063 Iustin Pop
701 a8083063 Iustin Pop
def IsValidShellParam(word):
702 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
703 a8083063 Iustin Pop

704 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
705 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
706 a8083063 Iustin Pop
  the actual command.
707 a8083063 Iustin Pop

708 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
709 a8083063 Iustin Pop
  side.
710 a8083063 Iustin Pop

711 58885d79 Iustin Pop
  @type word: str
712 58885d79 Iustin Pop
  @param word: the word to check
713 58885d79 Iustin Pop
  @rtype: boolean
714 58885d79 Iustin Pop
  @return: True if the word is 'safe'
715 58885d79 Iustin Pop

716 a8083063 Iustin Pop
  """
717 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
718 a8083063 Iustin Pop
719 a8083063 Iustin Pop
720 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
721 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
722 a8083063 Iustin Pop

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

728 58885d79 Iustin Pop
  @type template: str
729 58885d79 Iustin Pop
  @param template: the string holding the template for the
730 58885d79 Iustin Pop
      string formatting
731 58885d79 Iustin Pop
  @rtype: str
732 58885d79 Iustin Pop
  @return: the expanded command line
733 58885d79 Iustin Pop

734 a8083063 Iustin Pop
  """
735 a8083063 Iustin Pop
  for word in args:
736 a8083063 Iustin Pop
    if not IsValidShellParam(word):
737 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
738 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
739 a8083063 Iustin Pop
  return template % args
740 a8083063 Iustin Pop
741 a8083063 Iustin Pop
742 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
743 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
744 a8083063 Iustin Pop

745 58885d79 Iustin Pop
  @type value: int
746 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
747 9fbfbb7b Iustin Pop
  @type units: char
748 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
749 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
750 9fbfbb7b Iustin Pop
      - 'm' for MiBs
751 9fbfbb7b Iustin Pop
      - 'g' for GiBs
752 9fbfbb7b Iustin Pop
      - 't' for TiBs
753 58885d79 Iustin Pop
  @rtype: str
754 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
755 a8083063 Iustin Pop

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

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

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

826 58885d79 Iustin Pop
  @type file_name: str
827 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
828 58885d79 Iustin Pop
  @type key: str
829 58885d79 Iustin Pop
  @param key: string containing key
830 58885d79 Iustin Pop

831 a8083063 Iustin Pop
  """
832 a8083063 Iustin Pop
  key_fields = key.split()
833 a8083063 Iustin Pop
834 a8083063 Iustin Pop
  f = open(file_name, 'a+')
835 a8083063 Iustin Pop
  try:
836 a8083063 Iustin Pop
    nl = True
837 a8083063 Iustin Pop
    for line in f:
838 a8083063 Iustin Pop
      # Ignore whitespace changes
839 a8083063 Iustin Pop
      if line.split() == key_fields:
840 a8083063 Iustin Pop
        break
841 a8083063 Iustin Pop
      nl = line.endswith('\n')
842 a8083063 Iustin Pop
    else:
843 a8083063 Iustin Pop
      if not nl:
844 a8083063 Iustin Pop
        f.write("\n")
845 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
846 a8083063 Iustin Pop
      f.write("\n")
847 a8083063 Iustin Pop
      f.flush()
848 a8083063 Iustin Pop
  finally:
849 a8083063 Iustin Pop
    f.close()
850 a8083063 Iustin Pop
851 a8083063 Iustin Pop
852 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
853 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
854 a8083063 Iustin Pop

855 58885d79 Iustin Pop
  @type file_name: str
856 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
857 58885d79 Iustin Pop
  @type key: str
858 58885d79 Iustin Pop
  @param key: string containing key
859 58885d79 Iustin Pop

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

888 58885d79 Iustin Pop
  @type file_name: str
889 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
890 58885d79 Iustin Pop
  @type ip: str
891 58885d79 Iustin Pop
  @param ip: the IP address
892 58885d79 Iustin Pop
  @type hostname: str
893 58885d79 Iustin Pop
  @param hostname: the hostname to be added
894 58885d79 Iustin Pop
  @type aliases: list
895 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
896 58885d79 Iustin Pop

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

935 58885d79 Iustin Pop
  @type hostname: str
936 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
937 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
938 58885d79 Iustin Pop

939 d9c02ca6 Michael Hanselmann
  """
940 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
941 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
942 d9c02ca6 Michael Hanselmann
943 d9c02ca6 Michael Hanselmann
944 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
945 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
946 899d2a81 Michael Hanselmann

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

949 58885d79 Iustin Pop
  @type file_name: str
950 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
951 58885d79 Iustin Pop
  @type hostname: str
952 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
953 58885d79 Iustin Pop

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

991 58885d79 Iustin Pop
  @type hostname: str
992 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
993 58885d79 Iustin Pop
      full and shot name will be removed from
994 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
995 58885d79 Iustin Pop

996 d9c02ca6 Michael Hanselmann
  """
997 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
998 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
999 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1000 d9c02ca6 Michael Hanselmann
1001 d9c02ca6 Michael Hanselmann
1002 a8083063 Iustin Pop
def CreateBackup(file_name):
1003 a8083063 Iustin Pop
  """Creates a backup of a file.
1004 a8083063 Iustin Pop

1005 58885d79 Iustin Pop
  @type file_name: str
1006 58885d79 Iustin Pop
  @param file_name: file to be backed up
1007 58885d79 Iustin Pop
  @rtype: str
1008 58885d79 Iustin Pop
  @return: the path to the newly created backup
1009 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1010 a8083063 Iustin Pop

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

1036 58885d79 Iustin Pop
  @type value: str
1037 58885d79 Iustin Pop
  @param value: the argument to be quoted
1038 58885d79 Iustin Pop
  @rtype: str
1039 58885d79 Iustin Pop
  @return: the quoted value
1040 58885d79 Iustin Pop

1041 a8083063 Iustin Pop
  """
1042 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1043 a8083063 Iustin Pop
    return value
1044 a8083063 Iustin Pop
  else:
1045 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1046 a8083063 Iustin Pop
1047 a8083063 Iustin Pop
1048 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1049 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1050 58885d79 Iustin Pop

1051 58885d79 Iustin Pop
  @type args: list
1052 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1053 58885d79 Iustin Pop
  @rtype: str
1054 58885d79 Iustin Pop
  @return: the quoted arguments concatenaned with spaces
1055 a8083063 Iustin Pop

1056 a8083063 Iustin Pop
  """
1057 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1058 88d14415 Michael Hanselmann
1059 88d14415 Michael Hanselmann
1060 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1061 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1062 2c30e9d7 Alexander Schreiber

1063 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1064 58885d79 Iustin Pop
  to it.
1065 58885d79 Iustin Pop

1066 58885d79 Iustin Pop
  @type target: str
1067 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1068 58885d79 Iustin Pop
  @type port: int
1069 58885d79 Iustin Pop
  @param port: the port to connect to
1070 58885d79 Iustin Pop
  @type timeout: int
1071 58885d79 Iustin Pop
  @param timeout: the timeout on the connection attemp
1072 58885d79 Iustin Pop
  @type live_port_needed: boolean
1073 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1074 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1075 58885d79 Iustin Pop
  @type source: str or None
1076 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1077 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1078 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1079 2c30e9d7 Alexander Schreiber

1080 2c30e9d7 Alexander Schreiber
  """
1081 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1082 2c30e9d7 Alexander Schreiber
1083 0b5ad33e Iustin Pop
  success = False
1084 2c30e9d7 Alexander Schreiber
1085 b15d625f Iustin Pop
  if source is not None:
1086 b15d625f Iustin Pop
    try:
1087 b15d625f Iustin Pop
      sock.bind((source, 0))
1088 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
1089 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1090 b15d625f Iustin Pop
        success = False
1091 2c30e9d7 Alexander Schreiber
1092 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1093 2c30e9d7 Alexander Schreiber
1094 2c30e9d7 Alexander Schreiber
  try:
1095 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1096 2c30e9d7 Alexander Schreiber
    sock.close()
1097 2c30e9d7 Alexander Schreiber
    success = True
1098 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1099 2c30e9d7 Alexander Schreiber
    success = False
1100 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
1101 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1102 2c30e9d7 Alexander Schreiber
1103 2c30e9d7 Alexander Schreiber
  return success
1104 eedbda4b Michael Hanselmann
1105 eedbda4b Michael Hanselmann
1106 caad16e2 Iustin Pop
def OwnIpAddress(address):
1107 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1108 caad16e2 Iustin Pop

1109 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1110 caad16e2 Iustin Pop
  address.
1111 caad16e2 Iustin Pop

1112 caad16e2 Iustin Pop
  @type address: string
1113 caad16e2 Iustin Pop
  @param address: the addres to check
1114 caad16e2 Iustin Pop
  @rtype: bool
1115 58885d79 Iustin Pop
  @return: True if we own the address
1116 caad16e2 Iustin Pop

1117 caad16e2 Iustin Pop
  """
1118 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1119 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1120 caad16e2 Iustin Pop
1121 caad16e2 Iustin Pop
1122 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1123 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1124 58885d79 Iustin Pop

1125 58885d79 Iustin Pop
  @type path: str
1126 58885d79 Iustin Pop
  @param path: the directory to enumerate
1127 58885d79 Iustin Pop
  @rtype: list
1128 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1129 eedbda4b Michael Hanselmann

1130 eedbda4b Michael Hanselmann
  """
1131 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1132 f3299a07 Michael Hanselmann
  files.sort()
1133 f3299a07 Michael Hanselmann
  return files
1134 2f8b60b3 Iustin Pop
1135 2f8b60b3 Iustin Pop
1136 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1137 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1138 257f4c0a Iustin Pop

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

1143 2f8b60b3 Iustin Pop
  """
1144 2f8b60b3 Iustin Pop
  try:
1145 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1146 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1147 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1148 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1149 257f4c0a Iustin Pop
    else:
1150 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1151 257f4c0a Iustin Pop
                                   type(user))
1152 2f8b60b3 Iustin Pop
  except KeyError:
1153 2f8b60b3 Iustin Pop
    return default
1154 2f8b60b3 Iustin Pop
  return result.pw_dir
1155 59072e7e Michael Hanselmann
1156 59072e7e Michael Hanselmann
1157 24818e8f Michael Hanselmann
def NewUUID():
1158 59072e7e Michael Hanselmann
  """Returns a random UUID.
1159 59072e7e Michael Hanselmann

1160 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1161 58885d79 Iustin Pop
      filesystem.
1162 58885d79 Iustin Pop
  @rtype: str
1163 58885d79 Iustin Pop

1164 59072e7e Michael Hanselmann
  """
1165 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
1166 59072e7e Michael Hanselmann
  try:
1167 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
1168 59072e7e Michael Hanselmann
  finally:
1169 59072e7e Michael Hanselmann
    f.close()
1170 087b34fe Iustin Pop
1171 087b34fe Iustin Pop
1172 33081d90 Iustin Pop
def GenerateSecret():
1173 33081d90 Iustin Pop
  """Generates a random secret.
1174 33081d90 Iustin Pop

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

1178 58885d79 Iustin Pop
  @rtype: str
1179 58885d79 Iustin Pop
  @return: a sha1 hexdigest of a block of 64 random bytes
1180 58885d79 Iustin Pop

1181 33081d90 Iustin Pop
  """
1182 33081d90 Iustin Pop
  return sha.new(os.urandom(64)).hexdigest()
1183 33081d90 Iustin Pop
1184 33081d90 Iustin Pop
1185 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1186 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1187 9dae41ad Guido Trotter

1188 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1189 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1190 9dae41ad Guido Trotter

1191 9dae41ad Guido Trotter
  """
1192 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1193 9dae41ad Guido Trotter
    try:
1194 9dae41ad Guido Trotter
      os.mkdir(dir_name, mode)
1195 9dae41ad Guido Trotter
    except EnvironmentError, err:
1196 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1197 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1198 9dae41ad Guido Trotter
          " '%s': %s" % (dir_name, err))
1199 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1200 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1201 9dae41ad Guido Trotter
1202 9dae41ad Guido Trotter
1203 ca0aa6d0 Michael Hanselmann
def ReadFile(file_name, size=None):
1204 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1205 ca0aa6d0 Michael Hanselmann

1206 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1207 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1208 58885d79 Iustin Pop
  @rtype: str
1209 58885d79 Iustin Pop
  @return: the (possibly partial) conent of the file
1210 ca0aa6d0 Michael Hanselmann

1211 ca0aa6d0 Michael Hanselmann
  """
1212 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1213 ca0aa6d0 Michael Hanselmann
  try:
1214 ca0aa6d0 Michael Hanselmann
    if size is None:
1215 ca0aa6d0 Michael Hanselmann
      return f.read()
1216 ca0aa6d0 Michael Hanselmann
    else:
1217 ca0aa6d0 Michael Hanselmann
      return f.read(size)
1218 ca0aa6d0 Michael Hanselmann
  finally:
1219 ca0aa6d0 Michael Hanselmann
    f.close()
1220 ca0aa6d0 Michael Hanselmann
1221 ca0aa6d0 Michael Hanselmann
1222 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1223 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1224 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1225 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1226 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1227 087b34fe Iustin Pop
  """(Over)write a file atomically.
1228 087b34fe Iustin Pop

1229 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1230 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1231 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1232 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1233 087b34fe Iustin Pop
  mtime/atime of the file.
1234 087b34fe Iustin Pop

1235 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1236 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1237 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1238 087b34fe Iustin Pop
  temporary file should be removed.
1239 087b34fe Iustin Pop

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

1264 58885d79 Iustin Pop
  @rtype: None or int
1265 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1266 58885d79 Iustin Pop
      otherwise the file descriptor
1267 58885d79 Iustin Pop

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

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

1321 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1322 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1323 7b4126b7 Iustin Pop
  value, the index will be returned.
1324 7b4126b7 Iustin Pop

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

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

1330 58885d79 Iustin Pop
  @type seq: sequence
1331 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1332 58885d79 Iustin Pop
  @type base: int
1333 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1334 58885d79 Iustin Pop
  @rtype: int
1335 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1336 7b4126b7 Iustin Pop

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

1363 f7414041 Michael Hanselmann
  Element order is preserved.
1364 58885d79 Iustin Pop

1365 58885d79 Iustin Pop
  @type seq: sequence
1366 58885d79 Iustin Pop
  @param seq: the sequence with the source elementes
1367 58885d79 Iustin Pop
  @rtype: list
1368 58885d79 Iustin Pop
  @return: list of unique elements from seq
1369 58885d79 Iustin Pop

1370 f7414041 Michael Hanselmann
  """
1371 f7414041 Michael Hanselmann
  seen = set()
1372 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1373 1862d460 Alexander Schreiber
1374 1862d460 Alexander Schreiber
1375 1862d460 Alexander Schreiber
def IsValidMac(mac):
1376 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1377 1862d460 Alexander Schreiber

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

1381 58885d79 Iustin Pop
  @type mac: str
1382 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1383 58885d79 Iustin Pop
  @rtype: boolean
1384 58885d79 Iustin Pop
  @return: True is the MAC seems valid
1385 58885d79 Iustin Pop

1386 1862d460 Alexander Schreiber
  """
1387 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1388 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1389 06009e27 Iustin Pop
1390 06009e27 Iustin Pop
1391 06009e27 Iustin Pop
def TestDelay(duration):
1392 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1393 06009e27 Iustin Pop

1394 58885d79 Iustin Pop
  @type duration: float
1395 58885d79 Iustin Pop
  @param duration: the sleep duration
1396 58885d79 Iustin Pop
  @rtype: boolean
1397 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1398 58885d79 Iustin Pop

1399 06009e27 Iustin Pop
  """
1400 06009e27 Iustin Pop
  if duration < 0:
1401 06009e27 Iustin Pop
    return False
1402 06009e27 Iustin Pop
  time.sleep(duration)
1403 06009e27 Iustin Pop
  return True
1404 8f765069 Iustin Pop
1405 8f765069 Iustin Pop
1406 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1407 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1408 8f765069 Iustin Pop

1409 7d88772a Iustin Pop
  @type fd: int
1410 7d88772a Iustin Pop
  @param fd: the file descriptor
1411 7d88772a Iustin Pop
  @type retries: int
1412 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1413 7d88772a Iustin Pop
      other error than EBADF
1414 7d88772a Iustin Pop

1415 7d88772a Iustin Pop
  """
1416 7d88772a Iustin Pop
  try:
1417 7d88772a Iustin Pop
    os.close(fd)
1418 7d88772a Iustin Pop
  except OSError, err:
1419 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1420 7d88772a Iustin Pop
      if retries > 0:
1421 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1422 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1423 7d88772a Iustin Pop
    # ignore this and go on
1424 7d88772a Iustin Pop
1425 7d88772a Iustin Pop
1426 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1427 7d88772a Iustin Pop
  """Close file descriptors.
1428 7d88772a Iustin Pop

1429 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1430 7d88772a Iustin Pop
  stdin/out/err).
1431 8f765069 Iustin Pop

1432 58885d79 Iustin Pop
  @type noclose_fds: list or None
1433 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1434 58885d79 Iustin Pop
      that should not be closed
1435 58885d79 Iustin Pop

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

1461 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1462 7d88772a Iustin Pop
  runs it in the background as a daemon.
1463 7d88772a Iustin Pop

1464 7d88772a Iustin Pop
  @type logfile: str
1465 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1466 7d88772a Iustin Pop
  @rtype: int
1467 5fcc718f Iustin Pop
  @return: the value zero
1468 7d88772a Iustin Pop

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

1502 58885d79 Iustin Pop
  @type name: str
1503 58885d79 Iustin Pop
  @param name: the daemon name
1504 58885d79 Iustin Pop
  @rtype: str
1505 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1506 58885d79 Iustin Pop
      daemon name
1507 b330ac0b Guido Trotter

1508 b330ac0b Guido Trotter
  """
1509 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1510 b330ac0b Guido Trotter
1511 b330ac0b Guido Trotter
1512 b330ac0b Guido Trotter
def WritePidFile(name):
1513 b330ac0b Guido Trotter
  """Write the current process pidfile.
1514 b330ac0b Guido Trotter

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

1517 58885d79 Iustin Pop
  @type name: str
1518 58885d79 Iustin Pop
  @param name: the daemon name to use
1519 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1520 58885d79 Iustin Pop
      points to a live process
1521 b330ac0b Guido Trotter

1522 b330ac0b Guido Trotter
  """
1523 b330ac0b Guido Trotter
  pid = os.getpid()
1524 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1525 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1526 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1527 b330ac0b Guido Trotter
1528 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1529 b330ac0b Guido Trotter
1530 b330ac0b Guido Trotter
1531 b330ac0b Guido Trotter
def RemovePidFile(name):
1532 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1533 b330ac0b Guido Trotter

1534 b330ac0b Guido Trotter
  Any errors are ignored.
1535 b330ac0b Guido Trotter

1536 58885d79 Iustin Pop
  @type name: str
1537 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1538 58885d79 Iustin Pop

1539 b330ac0b Guido Trotter
  """
1540 b330ac0b Guido Trotter
  pid = os.getpid()
1541 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1542 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1543 b330ac0b Guido Trotter
  try:
1544 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1545 b330ac0b Guido Trotter
  except:
1546 b330ac0b Guido Trotter
    pass
1547 b330ac0b Guido Trotter
1548 b330ac0b Guido Trotter
1549 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1550 ff5251bc Iustin Pop
                waitpid=False):
1551 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1552 b2a1f511 Iustin Pop

1553 b2a1f511 Iustin Pop
  @type pid: int
1554 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1555 38206f3c Iustin Pop
  @type signal_: int
1556 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1557 b2a1f511 Iustin Pop
  @type timeout: int
1558 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1559 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1560 b2a1f511 Iustin Pop
                  will be done
1561 ff5251bc Iustin Pop
  @type waitpid: boolean
1562 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1563 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1564 ff5251bc Iustin Pop
      would remain as zombie
1565 b2a1f511 Iustin Pop

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

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

1612 58885d79 Iustin Pop
  @type name: str
1613 58885d79 Iustin Pop
  @param name: the name to look for
1614 58885d79 Iustin Pop
  @type search_path: str
1615 58885d79 Iustin Pop
  @param search_path: location to start at
1616 58885d79 Iustin Pop
  @type test: callable
1617 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1618 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1619 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1620 58885d79 Iustin Pop
  @rtype: str or None
1621 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1622 57c177af Iustin Pop

1623 57c177af Iustin Pop
  """
1624 57c177af Iustin Pop
  for dir_name in search_path:
1625 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1626 57c177af Iustin Pop
    if test(item_name):
1627 57c177af Iustin Pop
      return item_name
1628 57c177af Iustin Pop
  return None
1629 8d1a2a64 Michael Hanselmann
1630 8d1a2a64 Michael Hanselmann
1631 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1632 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1633 8d1a2a64 Michael Hanselmann

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

1637 58885d79 Iustin Pop
  @type vglist: dict
1638 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1639 58885d79 Iustin Pop
  @type vgname: str
1640 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1641 58885d79 Iustin Pop
  @type minsize: int
1642 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1643 58885d79 Iustin Pop
  @rtype: None or str
1644 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1645 8d1a2a64 Michael Hanselmann

1646 8d1a2a64 Michael Hanselmann
  """
1647 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1648 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1649 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1650 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1651 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1652 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1653 8d1a2a64 Michael Hanselmann
  return None
1654 7996a135 Iustin Pop
1655 7996a135 Iustin Pop
1656 45bc5e4a Michael Hanselmann
def SplitTime(value):
1657 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1658 739be818 Michael Hanselmann

1659 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1660 45bc5e4a Michael Hanselmann
  @type value: int or float
1661 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1662 739be818 Michael Hanselmann

1663 739be818 Michael Hanselmann
  """
1664 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1665 45bc5e4a Michael Hanselmann
1666 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1667 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1668 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1669 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1670 45bc5e4a Michael Hanselmann
1671 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1672 739be818 Michael Hanselmann
1673 739be818 Michael Hanselmann
1674 739be818 Michael Hanselmann
def MergeTime(timetuple):
1675 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1676 739be818 Michael Hanselmann

1677 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1678 739be818 Michael Hanselmann
  @type timetuple: tuple
1679 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1680 739be818 Michael Hanselmann

1681 739be818 Michael Hanselmann
  """
1682 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1683 739be818 Michael Hanselmann
1684 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1685 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1686 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1687 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1688 739be818 Michael Hanselmann
1689 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1690 739be818 Michael Hanselmann
1691 739be818 Michael Hanselmann
1692 4a8b186a Michael Hanselmann
def GetNodeDaemonPort():
1693 4a8b186a Michael Hanselmann
  """Get the node daemon port for this cluster.
1694 4a8b186a Michael Hanselmann

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

1699 58885d79 Iustin Pop
  @rtype: int
1700 58885d79 Iustin Pop

1701 4a8b186a Michael Hanselmann
  """
1702 4a8b186a Michael Hanselmann
  try:
1703 4a8b186a Michael Hanselmann
    port = socket.getservbyname("ganeti-noded", "tcp")
1704 4a8b186a Michael Hanselmann
  except socket.error:
1705 4a8b186a Michael Hanselmann
    port = constants.DEFAULT_NODED_PORT
1706 4a8b186a Michael Hanselmann
1707 4a8b186a Michael Hanselmann
  return port
1708 4a8b186a Michael Hanselmann
1709 4a8b186a Michael Hanselmann
1710 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1711 d21d09d6 Iustin Pop
                 multithreaded=False):
1712 82d9caef Iustin Pop
  """Configures the logging module.
1713 82d9caef Iustin Pop

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

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

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

1781 f65f63ef Iustin Pop
  @param fname: the file name
1782 f65f63ef Iustin Pop
  @type lines: int
1783 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1784 f65f63ef Iustin Pop

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

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

1810 26f15862 Iustin Pop
  @type text: str or unicode
1811 26f15862 Iustin Pop
  @param text: input data
1812 26f15862 Iustin Pop
  @rtype: str
1813 26f15862 Iustin Pop
  @return: a safe version of text
1814 26f15862 Iustin Pop

1815 26f15862 Iustin Pop
  """
1816 26f15862 Iustin Pop
  text = text.encode('ascii', 'backslashreplace')
1817 26f15862 Iustin Pop
  text = text.encode('string_escape')
1818 26f15862 Iustin Pop
  return text
1819 26f15862 Iustin Pop
1820 26f15862 Iustin Pop
1821 7996a135 Iustin Pop
def LockedMethod(fn):
1822 7996a135 Iustin Pop
  """Synchronized object access decorator.
1823 7996a135 Iustin Pop

1824 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1825 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1826 7996a135 Iustin Pop

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

1851 58885d79 Iustin Pop
  @type fd: int
1852 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1853 58885d79 Iustin Pop

1854 eb0f0ce0 Michael Hanselmann
  """
1855 eb0f0ce0 Michael Hanselmann
  try:
1856 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1857 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1858 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1859 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1860 eb0f0ce0 Michael Hanselmann
    raise
1861 de499029 Michael Hanselmann
1862 de499029 Michael Hanselmann
1863 a87b4824 Michael Hanselmann
class FileLock(object):
1864 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1865 a87b4824 Michael Hanselmann

1866 a87b4824 Michael Hanselmann
  """
1867 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1868 58885d79 Iustin Pop
    """Constructor for FileLock.
1869 58885d79 Iustin Pop

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

1872 58885d79 Iustin Pop
    @type filename: str
1873 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1874 58885d79 Iustin Pop

1875 58885d79 Iustin Pop
    """
1876 a87b4824 Michael Hanselmann
    self.filename = filename
1877 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1878 a87b4824 Michael Hanselmann
1879 a87b4824 Michael Hanselmann
  def __del__(self):
1880 a87b4824 Michael Hanselmann
    self.Close()
1881 a87b4824 Michael Hanselmann
1882 a87b4824 Michael Hanselmann
  def Close(self):
1883 58885d79 Iustin Pop
    """Close the file and release the lock.
1884 58885d79 Iustin Pop

1885 58885d79 Iustin Pop
    """
1886 a87b4824 Michael Hanselmann
    if self.fd:
1887 a87b4824 Michael Hanselmann
      self.fd.close()
1888 a87b4824 Michael Hanselmann
      self.fd = None
1889 a87b4824 Michael Hanselmann
1890 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
1891 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
1892 aa74b828 Michael Hanselmann

1893 aa74b828 Michael Hanselmann
    @type flag: int
1894 58885d79 Iustin Pop
    @param flag: operation flag
1895 aa74b828 Michael Hanselmann
    @type blocking: bool
1896 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
1897 aa74b828 Michael Hanselmann
    @type timeout: None or float
1898 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
1899 aa74b828 Michael Hanselmann
                    non-blocking mode).
1900 aa74b828 Michael Hanselmann
    @type errmsg: string
1901 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
1902 aa74b828 Michael Hanselmann

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

1936 58885d79 Iustin Pop
    @type blocking: boolean
1937 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1938 58885d79 Iustin Pop
        can lock the file or return immediately
1939 58885d79 Iustin Pop
    @type timeout: int or None
1940 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1941 58885d79 Iustin Pop
        (in blocking mode)
1942 58885d79 Iustin Pop

1943 a87b4824 Michael Hanselmann
    """
1944 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
1945 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1946 a87b4824 Michael Hanselmann
1947 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
1948 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1949 a87b4824 Michael Hanselmann

1950 58885d79 Iustin Pop
    @type blocking: boolean
1951 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1952 58885d79 Iustin Pop
        can lock the file or return immediately
1953 58885d79 Iustin Pop
    @type timeout: int or None
1954 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1955 58885d79 Iustin Pop
        (in blocking mode)
1956 58885d79 Iustin Pop

1957 a87b4824 Michael Hanselmann
    """
1958 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
1959 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1960 a87b4824 Michael Hanselmann
1961 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
1962 a87b4824 Michael Hanselmann
    """Unlocks the file.
1963 a87b4824 Michael Hanselmann

1964 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
1965 58885d79 Iustin Pop
    operation::
1966 58885d79 Iustin Pop

1967 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
1968 58885d79 Iustin Pop
      operations.
1969 58885d79 Iustin Pop

1970 58885d79 Iustin Pop
    @type blocking: boolean
1971 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1972 58885d79 Iustin Pop
        can lock the file or return immediately
1973 58885d79 Iustin Pop
    @type timeout: int or None
1974 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1975 58885d79 Iustin Pop
        (in blocking mode)
1976 a87b4824 Michael Hanselmann

1977 a87b4824 Michael Hanselmann
    """
1978 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
1979 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
1980 a87b4824 Michael Hanselmann
1981 a87b4824 Michael Hanselmann
1982 de499029 Michael Hanselmann
class SignalHandler(object):
1983 de499029 Michael Hanselmann
  """Generic signal handler class.
1984 de499029 Michael Hanselmann

1985 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
1986 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
1987 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
1988 58885d79 Iustin Pop
  signal was sent.
1989 58885d79 Iustin Pop

1990 58885d79 Iustin Pop
  @type signum: list
1991 58885d79 Iustin Pop
  @ivar signum: the signals we handle
1992 58885d79 Iustin Pop
  @type called: boolean
1993 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
1994 de499029 Michael Hanselmann

1995 de499029 Michael Hanselmann
  """
1996 de499029 Michael Hanselmann
  def __init__(self, signum):
1997 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1998 de499029 Michael Hanselmann

1999 58885d79 Iustin Pop
    @type signum: int or list of ints
2000 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2001 de499029 Michael Hanselmann

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

2034 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2035 58885d79 Iustin Pop

2036 de499029 Michael Hanselmann
    """
2037 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2038 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2039 de499029 Michael Hanselmann
      # If successful, remove from dict
2040 de499029 Michael Hanselmann
      del self._previous[signum]
2041 de499029 Michael Hanselmann
2042 de499029 Michael Hanselmann
  def Clear(self):
2043 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2044 de499029 Michael Hanselmann

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

2047 de499029 Michael Hanselmann
    """
2048 de499029 Michael Hanselmann
    self.called = False
2049 de499029 Michael Hanselmann
2050 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
2051 de499029 Michael Hanselmann
    """Actual signal handling function.
2052 de499029 Michael Hanselmann

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

2062 a2d2e1a7 Iustin Pop
  Among the features are:
2063 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2064 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2065 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2066 a2d2e1a7 Iustin Pop

2067 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2068 a2d2e1a7 Iustin Pop

2069 a2d2e1a7 Iustin Pop
  """
2070 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2071 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2072 a2d2e1a7 Iustin Pop
2073 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2074 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2075 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2076 a2d2e1a7 Iustin Pop
2077 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2078 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2079 a2d2e1a7 Iustin Pop

2080 a2d2e1a7 Iustin Pop
    @type field: str
2081 a2d2e1a7 Iustin Pop
    @param field: the string to match
2082 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2083 a2d2e1a7 Iustin Pop

2084 a2d2e1a7 Iustin Pop
    """
2085 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2086 a2d2e1a7 Iustin Pop
      return m
2087 a2d2e1a7 Iustin Pop
    return False
2088 a2d2e1a7 Iustin Pop
2089 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2090 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2091 a2d2e1a7 Iustin Pop

2092 a2d2e1a7 Iustin Pop
    @type items: list
2093 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2094 a2d2e1a7 Iustin Pop
    @rtype: list
2095 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2096 a2d2e1a7 Iustin Pop

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