Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 5b69bc7c

History | View | Annotate | Download (66.2 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

313 6e797216 Michael Hanselmann
  """
314 6e797216 Michael Hanselmann
  try:
315 6e797216 Michael Hanselmann
    return os.rename(old, new)
316 6e797216 Michael Hanselmann
  except OSError, err:
317 6e797216 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
318 6e797216 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
319 6e797216 Michael Hanselmann
    # as efficient.
320 6e797216 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
321 6e797216 Michael Hanselmann
      # Create directory and try again
322 a426508d Michael Hanselmann
      dirname = os.path.dirname(new)
323 a426508d Michael Hanselmann
      try:
324 a426508d Michael Hanselmann
        os.makedirs(dirname, mode=mkdir_mode)
325 a426508d Michael Hanselmann
      except OSError, err:
326 a426508d Michael Hanselmann
        # Ignore EEXIST. This is only handled in os.makedirs as included in
327 a426508d Michael Hanselmann
        # Python 2.5 and above.
328 a426508d Michael Hanselmann
        if err.errno != errno.EEXIST or not os.path.exists(dirname):
329 a426508d Michael Hanselmann
          raise
330 a426508d Michael Hanselmann
331 6e797216 Michael Hanselmann
      return os.rename(old, new)
332 a426508d Michael Hanselmann
333 6e797216 Michael Hanselmann
    raise
334 6e797216 Michael Hanselmann
335 6e797216 Michael Hanselmann
336 a8083063 Iustin Pop
def _FingerprintFile(filename):
337 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
338 a8083063 Iustin Pop

339 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
340 a8083063 Iustin Pop
  instead.
341 a8083063 Iustin Pop

342 58885d79 Iustin Pop
  @type filename: str
343 58885d79 Iustin Pop
  @param filename: the filename to checksum
344 58885d79 Iustin Pop
  @rtype: str
345 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
346 58885d79 Iustin Pop
      of the file
347 a8083063 Iustin Pop

348 a8083063 Iustin Pop
  """
349 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
350 a8083063 Iustin Pop
    return None
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
  f = open(filename)
353 a8083063 Iustin Pop
354 7ffe8fba Carlos Valiente
  fp = sha1()
355 a8083063 Iustin Pop
  while True:
356 a8083063 Iustin Pop
    data = f.read(4096)
357 a8083063 Iustin Pop
    if not data:
358 a8083063 Iustin Pop
      break
359 a8083063 Iustin Pop
360 a8083063 Iustin Pop
    fp.update(data)
361 a8083063 Iustin Pop
362 a8083063 Iustin Pop
  return fp.hexdigest()
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
365 a8083063 Iustin Pop
def FingerprintFiles(files):
366 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
367 a8083063 Iustin Pop

368 58885d79 Iustin Pop
  @type files: list
369 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
370 58885d79 Iustin Pop
  @rtype: dict
371 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
372 58885d79 Iustin Pop
      existing files
373 a8083063 Iustin Pop

374 a8083063 Iustin Pop
  """
375 a8083063 Iustin Pop
  ret = {}
376 a8083063 Iustin Pop
377 a8083063 Iustin Pop
  for filename in files:
378 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
379 a8083063 Iustin Pop
    if cksum:
380 a8083063 Iustin Pop
      ret[filename] = cksum
381 a8083063 Iustin Pop
382 a8083063 Iustin Pop
  return ret
383 a8083063 Iustin Pop
384 a8083063 Iustin Pop
385 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
386 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
387 a5728081 Guido Trotter

388 a5728081 Guido Trotter
  @type target: dict
389 a5728081 Guido Trotter
  @param target: the dict to update
390 a5728081 Guido Trotter
  @type key_types: dict
391 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
392 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
393 a5728081 Guido Trotter
  @type allowed_values: list
394 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
395 a5728081 Guido Trotter

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

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

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

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

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

504 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
505 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
506 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
507 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
508 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
509 3a541d90 Iustin Pop
  'test1.example.org']}), except when the key fully matches an entry
510 3a541d90 Iustin Pop
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
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 256eb94b Guido Trotter
  @type case_sensitive: boolean
517 256eb94b Guido Trotter
  @param case_sensitive: whether to provide a case-sensitive match
518 a8083063 Iustin Pop

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

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

550 bcf043c9 Iustin Pop
  """
551 89e1fc26 Iustin Pop
  def __init__(self, name=None):
552 bcf043c9 Iustin Pop
    """Initialize the host name object.
553 bcf043c9 Iustin Pop

554 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
555 89e1fc26 Iustin Pop
    name.
556 bcf043c9 Iustin Pop

557 bcf043c9 Iustin Pop
    """
558 89e1fc26 Iustin Pop
    if name is None:
559 89e1fc26 Iustin Pop
      name = self.SysName()
560 89e1fc26 Iustin Pop
561 89e1fc26 Iustin Pop
    self.query = name
562 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
563 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
564 bcf043c9 Iustin Pop
565 c8a0948f Michael Hanselmann
  def ShortName(self):
566 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
567 c8a0948f Michael Hanselmann

568 c8a0948f Michael Hanselmann
    """
569 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
570 c8a0948f Michael Hanselmann
571 89e1fc26 Iustin Pop
  @staticmethod
572 89e1fc26 Iustin Pop
  def SysName():
573 89e1fc26 Iustin Pop
    """Return the current system's name.
574 bcf043c9 Iustin Pop

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

577 89e1fc26 Iustin Pop
    """
578 89e1fc26 Iustin Pop
    return socket.gethostname()
579 a8083063 Iustin Pop
580 89e1fc26 Iustin Pop
  @staticmethod
581 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
582 89e1fc26 Iustin Pop
    """Look up hostname
583 a8083063 Iustin Pop

584 58885d79 Iustin Pop
    @type hostname: str
585 58885d79 Iustin Pop
    @param hostname: hostname to look up
586 89e1fc26 Iustin Pop

587 58885d79 Iustin Pop
    @rtype: tuple
588 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
589 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
590 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
591 89e1fc26 Iustin Pop

592 89e1fc26 Iustin Pop
    """
593 89e1fc26 Iustin Pop
    try:
594 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
595 89e1fc26 Iustin Pop
    except socket.gaierror, err:
596 89e1fc26 Iustin Pop
      # hostname not found in DNS
597 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
598 a8083063 Iustin Pop
599 89e1fc26 Iustin Pop
    return result
600 a8083063 Iustin Pop
601 a8083063 Iustin Pop
602 104f4ca1 Iustin Pop
def GetHostInfo(name=None):
603 104f4ca1 Iustin Pop
  """Lookup host name and raise an OpPrereqError for failures"""
604 104f4ca1 Iustin Pop
605 104f4ca1 Iustin Pop
  try:
606 104f4ca1 Iustin Pop
    return HostInfo(name)
607 104f4ca1 Iustin Pop
  except errors.ResolverError, err:
608 104f4ca1 Iustin Pop
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
609 104f4ca1 Iustin Pop
                               (err[0], err[2]), errors.ECODE_RESOLVER)
610 104f4ca1 Iustin Pop
611 104f4ca1 Iustin Pop
612 a8083063 Iustin Pop
def ListVolumeGroups():
613 a8083063 Iustin Pop
  """List volume groups and their size
614 a8083063 Iustin Pop

615 58885d79 Iustin Pop
  @rtype: dict
616 58885d79 Iustin Pop
  @return:
617 58885d79 Iustin Pop
       Dictionary with keys volume name and values
618 58885d79 Iustin Pop
       the size of the volume
619 a8083063 Iustin Pop

620 a8083063 Iustin Pop
  """
621 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
622 a8083063 Iustin Pop
  result = RunCmd(command)
623 a8083063 Iustin Pop
  retval = {}
624 a8083063 Iustin Pop
  if result.failed:
625 a8083063 Iustin Pop
    return retval
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
628 a8083063 Iustin Pop
    try:
629 a8083063 Iustin Pop
      name, size = line.split()
630 a8083063 Iustin Pop
      size = int(float(size))
631 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
632 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
633 a8083063 Iustin Pop
      continue
634 a8083063 Iustin Pop
635 a8083063 Iustin Pop
    retval[name] = size
636 a8083063 Iustin Pop
637 a8083063 Iustin Pop
  return retval
638 a8083063 Iustin Pop
639 a8083063 Iustin Pop
640 a8083063 Iustin Pop
def BridgeExists(bridge):
641 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
642 a8083063 Iustin Pop

643 58885d79 Iustin Pop
  @type bridge: str
644 58885d79 Iustin Pop
  @param bridge: the bridge name to check
645 58885d79 Iustin Pop
  @rtype: boolean
646 58885d79 Iustin Pop
  @return: True if it does
647 a8083063 Iustin Pop

648 a8083063 Iustin Pop
  """
649 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
650 a8083063 Iustin Pop
651 a8083063 Iustin Pop
652 a8083063 Iustin Pop
def NiceSort(name_list):
653 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
654 a8083063 Iustin Pop

655 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
656 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
657 58885d79 Iustin Pop
  'a11']}.
658 a8083063 Iustin Pop

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

663 58885d79 Iustin Pop
  @type name_list: list
664 58885d79 Iustin Pop
  @param name_list: the names to be sorted
665 58885d79 Iustin Pop
  @rtype: list
666 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
667 a8083063 Iustin Pop

668 a8083063 Iustin Pop
  """
669 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
670 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
671 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
672 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
673 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
674 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
675 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
676 a8083063 Iustin Pop
  def _TryInt(val):
677 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
678 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
679 a8083063 Iustin Pop
      return val
680 a8083063 Iustin Pop
    rval = int(val)
681 a8083063 Iustin Pop
    return rval
682 a8083063 Iustin Pop
683 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
684 a8083063 Iustin Pop
             for name in name_list]
685 a8083063 Iustin Pop
  to_sort.sort()
686 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
687 a8083063 Iustin Pop
688 a8083063 Iustin Pop
689 a8083063 Iustin Pop
def TryConvert(fn, val):
690 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
691 a8083063 Iustin Pop

692 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
693 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
694 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
695 58885d79 Iustin Pop
  exceptions are propagated to the caller.
696 58885d79 Iustin Pop

697 58885d79 Iustin Pop
  @type fn: callable
698 58885d79 Iustin Pop
  @param fn: function to apply to the value
699 58885d79 Iustin Pop
  @param val: the value to be converted
700 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
701 58885d79 Iustin Pop
      otherwise the original value.
702 a8083063 Iustin Pop

703 a8083063 Iustin Pop
  """
704 a8083063 Iustin Pop
  try:
705 a8083063 Iustin Pop
    nv = fn(val)
706 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
707 a8083063 Iustin Pop
    nv = val
708 a8083063 Iustin Pop
  return nv
709 a8083063 Iustin Pop
710 a8083063 Iustin Pop
711 a8083063 Iustin Pop
def IsValidIP(ip):
712 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
713 a8083063 Iustin Pop

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

717 58885d79 Iustin Pop
  @type ip: str
718 58885d79 Iustin Pop
  @param ip: the address to be checked
719 58885d79 Iustin Pop
  @rtype: a regular expression match object
720 5bbd3f7f Michael Hanselmann
  @return: a regular expression match object, or None if the
721 58885d79 Iustin Pop
      address is not valid
722 a8083063 Iustin Pop

723 a8083063 Iustin Pop
  """
724 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
725 58885d79 Iustin Pop
  #TODO: convert and return only boolean
726 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
727 a8083063 Iustin Pop
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
def IsValidShellParam(word):
730 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
731 a8083063 Iustin Pop

732 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
733 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
734 a8083063 Iustin Pop
  the actual command.
735 a8083063 Iustin Pop

736 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
737 a8083063 Iustin Pop
  side.
738 a8083063 Iustin Pop

739 58885d79 Iustin Pop
  @type word: str
740 58885d79 Iustin Pop
  @param word: the word to check
741 58885d79 Iustin Pop
  @rtype: boolean
742 58885d79 Iustin Pop
  @return: True if the word is 'safe'
743 58885d79 Iustin Pop

744 a8083063 Iustin Pop
  """
745 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
746 a8083063 Iustin Pop
747 a8083063 Iustin Pop
748 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
749 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
750 a8083063 Iustin Pop

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

756 58885d79 Iustin Pop
  @type template: str
757 58885d79 Iustin Pop
  @param template: the string holding the template for the
758 58885d79 Iustin Pop
      string formatting
759 58885d79 Iustin Pop
  @rtype: str
760 58885d79 Iustin Pop
  @return: the expanded command line
761 58885d79 Iustin Pop

762 a8083063 Iustin Pop
  """
763 a8083063 Iustin Pop
  for word in args:
764 a8083063 Iustin Pop
    if not IsValidShellParam(word):
765 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
766 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
767 a8083063 Iustin Pop
  return template % args
768 a8083063 Iustin Pop
769 a8083063 Iustin Pop
770 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
771 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
772 a8083063 Iustin Pop

773 58885d79 Iustin Pop
  @type value: int
774 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
775 9fbfbb7b Iustin Pop
  @type units: char
776 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
777 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
778 9fbfbb7b Iustin Pop
      - 'm' for MiBs
779 9fbfbb7b Iustin Pop
      - 'g' for GiBs
780 9fbfbb7b Iustin Pop
      - 't' for TiBs
781 58885d79 Iustin Pop
  @rtype: str
782 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
783 a8083063 Iustin Pop

784 a8083063 Iustin Pop
  """
785 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
786 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
787 a8083063 Iustin Pop
788 9fbfbb7b Iustin Pop
  suffix = ''
789 9fbfbb7b Iustin Pop
790 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
791 9fbfbb7b Iustin Pop
    if units == 'h':
792 9fbfbb7b Iustin Pop
      suffix = 'M'
793 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
794 9fbfbb7b Iustin Pop
795 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
796 9fbfbb7b Iustin Pop
    if units == 'h':
797 9fbfbb7b Iustin Pop
      suffix = 'G'
798 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
799 a8083063 Iustin Pop
800 a8083063 Iustin Pop
  else:
801 9fbfbb7b Iustin Pop
    if units == 'h':
802 9fbfbb7b Iustin Pop
      suffix = 'T'
803 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
804 a8083063 Iustin Pop
805 a8083063 Iustin Pop
806 a8083063 Iustin Pop
def ParseUnit(input_string):
807 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
808 a8083063 Iustin Pop

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

813 a8083063 Iustin Pop
  """
814 9939547b Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
815 a8083063 Iustin Pop
  if not m:
816 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
817 a8083063 Iustin Pop
818 a8083063 Iustin Pop
  value = float(m.groups()[0])
819 a8083063 Iustin Pop
820 a8083063 Iustin Pop
  unit = m.groups()[1]
821 a8083063 Iustin Pop
  if unit:
822 a8083063 Iustin Pop
    lcunit = unit.lower()
823 a8083063 Iustin Pop
  else:
824 a8083063 Iustin Pop
    lcunit = 'm'
825 a8083063 Iustin Pop
826 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
827 a8083063 Iustin Pop
    # Value already in MiB
828 a8083063 Iustin Pop
    pass
829 a8083063 Iustin Pop
830 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
831 a8083063 Iustin Pop
    value *= 1024
832 a8083063 Iustin Pop
833 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
834 a8083063 Iustin Pop
    value *= 1024 * 1024
835 a8083063 Iustin Pop
836 a8083063 Iustin Pop
  else:
837 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
838 a8083063 Iustin Pop
839 a8083063 Iustin Pop
  # Make sure we round up
840 a8083063 Iustin Pop
  if int(value) < value:
841 a8083063 Iustin Pop
    value += 1
842 a8083063 Iustin Pop
843 a8083063 Iustin Pop
  # Round up to the next multiple of 4
844 a8083063 Iustin Pop
  value = int(value)
845 a8083063 Iustin Pop
  if value % 4:
846 a8083063 Iustin Pop
    value += 4 - value % 4
847 a8083063 Iustin Pop
848 a8083063 Iustin Pop
  return value
849 a8083063 Iustin Pop
850 a8083063 Iustin Pop
851 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
852 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
853 a8083063 Iustin Pop

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

859 a8083063 Iustin Pop
  """
860 a8083063 Iustin Pop
  key_fields = key.split()
861 a8083063 Iustin Pop
862 a8083063 Iustin Pop
  f = open(file_name, 'a+')
863 a8083063 Iustin Pop
  try:
864 a8083063 Iustin Pop
    nl = True
865 a8083063 Iustin Pop
    for line in f:
866 a8083063 Iustin Pop
      # Ignore whitespace changes
867 a8083063 Iustin Pop
      if line.split() == key_fields:
868 a8083063 Iustin Pop
        break
869 a8083063 Iustin Pop
      nl = line.endswith('\n')
870 a8083063 Iustin Pop
    else:
871 a8083063 Iustin Pop
      if not nl:
872 a8083063 Iustin Pop
        f.write("\n")
873 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
874 a8083063 Iustin Pop
      f.write("\n")
875 a8083063 Iustin Pop
      f.flush()
876 a8083063 Iustin Pop
  finally:
877 a8083063 Iustin Pop
    f.close()
878 a8083063 Iustin Pop
879 a8083063 Iustin Pop
880 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
881 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
882 a8083063 Iustin Pop

883 58885d79 Iustin Pop
  @type file_name: str
884 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
885 58885d79 Iustin Pop
  @type key: str
886 58885d79 Iustin Pop
  @param key: string containing key
887 58885d79 Iustin Pop

888 a8083063 Iustin Pop
  """
889 a8083063 Iustin Pop
  key_fields = key.split()
890 a8083063 Iustin Pop
891 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
892 a8083063 Iustin Pop
  try:
893 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
894 a8083063 Iustin Pop
    try:
895 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
896 59f82e3f Michael Hanselmann
      try:
897 59f82e3f Michael Hanselmann
        for line in f:
898 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
899 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
900 59f82e3f Michael Hanselmann
            out.write(line)
901 899d2a81 Michael Hanselmann
902 899d2a81 Michael Hanselmann
        out.flush()
903 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
904 899d2a81 Michael Hanselmann
      finally:
905 899d2a81 Michael Hanselmann
        f.close()
906 899d2a81 Michael Hanselmann
    finally:
907 899d2a81 Michael Hanselmann
      out.close()
908 899d2a81 Michael Hanselmann
  except:
909 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
910 899d2a81 Michael Hanselmann
    raise
911 899d2a81 Michael Hanselmann
912 899d2a81 Michael Hanselmann
913 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
914 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
915 899d2a81 Michael Hanselmann

916 58885d79 Iustin Pop
  @type file_name: str
917 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
918 58885d79 Iustin Pop
  @type ip: str
919 58885d79 Iustin Pop
  @param ip: the IP address
920 58885d79 Iustin Pop
  @type hostname: str
921 58885d79 Iustin Pop
  @param hostname: the hostname to be added
922 58885d79 Iustin Pop
  @type aliases: list
923 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
924 58885d79 Iustin Pop

925 899d2a81 Michael Hanselmann
  """
926 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
927 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
928 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
929 7fbb1f65 Michael Hanselmann
930 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
931 899d2a81 Michael Hanselmann
  try:
932 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
933 9440aeab Michael Hanselmann
    try:
934 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
935 9440aeab Michael Hanselmann
      try:
936 9440aeab Michael Hanselmann
        for line in f:
937 9440aeab Michael Hanselmann
          fields = line.split()
938 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
939 9440aeab Michael Hanselmann
            continue
940 9440aeab Michael Hanselmann
          out.write(line)
941 9440aeab Michael Hanselmann
942 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
943 9440aeab Michael Hanselmann
        if aliases:
944 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
945 9440aeab Michael Hanselmann
        out.write('\n')
946 9440aeab Michael Hanselmann
947 9440aeab Michael Hanselmann
        out.flush()
948 2e3e75b7 Michael Hanselmann
        os.fsync(out)
949 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
950 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
951 9440aeab Michael Hanselmann
      finally:
952 9440aeab Michael Hanselmann
        f.close()
953 9440aeab Michael Hanselmann
    finally:
954 9440aeab Michael Hanselmann
      out.close()
955 9440aeab Michael Hanselmann
  except:
956 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
957 9440aeab Michael Hanselmann
    raise
958 899d2a81 Michael Hanselmann
959 899d2a81 Michael Hanselmann
960 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
961 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
962 d9c02ca6 Michael Hanselmann

963 58885d79 Iustin Pop
  @type hostname: str
964 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
965 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
966 58885d79 Iustin Pop

967 d9c02ca6 Michael Hanselmann
  """
968 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
969 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
970 d9c02ca6 Michael Hanselmann
971 d9c02ca6 Michael Hanselmann
972 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
973 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
974 899d2a81 Michael Hanselmann

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

977 58885d79 Iustin Pop
  @type file_name: str
978 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
979 58885d79 Iustin Pop
  @type hostname: str
980 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
981 58885d79 Iustin Pop

982 899d2a81 Michael Hanselmann
  """
983 9b977740 Guido Trotter
  # FIXME: use WriteFile + fn rather than duplicating its efforts
984 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
985 899d2a81 Michael Hanselmann
  try:
986 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
987 899d2a81 Michael Hanselmann
    try:
988 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
989 899d2a81 Michael Hanselmann
      try:
990 899d2a81 Michael Hanselmann
        for line in f:
991 899d2a81 Michael Hanselmann
          fields = line.split()
992 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
993 899d2a81 Michael Hanselmann
            names = fields[1:]
994 899d2a81 Michael Hanselmann
            if hostname in names:
995 899d2a81 Michael Hanselmann
              while hostname in names:
996 899d2a81 Michael Hanselmann
                names.remove(hostname)
997 899d2a81 Michael Hanselmann
              if names:
998 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
999 899d2a81 Michael Hanselmann
              continue
1000 899d2a81 Michael Hanselmann
1001 899d2a81 Michael Hanselmann
          out.write(line)
1002 59f82e3f Michael Hanselmann
1003 59f82e3f Michael Hanselmann
        out.flush()
1004 2e3e75b7 Michael Hanselmann
        os.fsync(out)
1005 9b977740 Guido Trotter
        os.chmod(tmpname, 0644)
1006 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
1007 59f82e3f Michael Hanselmann
      finally:
1008 59f82e3f Michael Hanselmann
        f.close()
1009 a8083063 Iustin Pop
    finally:
1010 59f82e3f Michael Hanselmann
      out.close()
1011 59f82e3f Michael Hanselmann
  except:
1012 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
1013 59f82e3f Michael Hanselmann
    raise
1014 a8083063 Iustin Pop
1015 a8083063 Iustin Pop
1016 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
1017 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
1018 d9c02ca6 Michael Hanselmann

1019 58885d79 Iustin Pop
  @type hostname: str
1020 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
1021 58885d79 Iustin Pop
      full and shot name will be removed from
1022 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
1023 58885d79 Iustin Pop

1024 d9c02ca6 Michael Hanselmann
  """
1025 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
1026 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1027 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1028 d9c02ca6 Michael Hanselmann
1029 d9c02ca6 Michael Hanselmann
1030 a8083063 Iustin Pop
def CreateBackup(file_name):
1031 a8083063 Iustin Pop
  """Creates a backup of a file.
1032 a8083063 Iustin Pop

1033 58885d79 Iustin Pop
  @type file_name: str
1034 58885d79 Iustin Pop
  @param file_name: file to be backed up
1035 58885d79 Iustin Pop
  @rtype: str
1036 58885d79 Iustin Pop
  @return: the path to the newly created backup
1037 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
1038 a8083063 Iustin Pop

1039 a8083063 Iustin Pop
  """
1040 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
1041 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1042 3ecf6786 Iustin Pop
                                file_name)
1043 a8083063 Iustin Pop
1044 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
1045 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
1046 081b1e69 Michael Hanselmann
1047 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
1048 081b1e69 Michael Hanselmann
  try:
1049 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1050 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
1051 081b1e69 Michael Hanselmann
    try:
1052 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
1053 081b1e69 Michael Hanselmann
    finally:
1054 081b1e69 Michael Hanselmann
      fdst.close()
1055 081b1e69 Michael Hanselmann
  finally:
1056 081b1e69 Michael Hanselmann
    fsrc.close()
1057 081b1e69 Michael Hanselmann
1058 a8083063 Iustin Pop
  return backup_name
1059 a8083063 Iustin Pop
1060 a8083063 Iustin Pop
1061 a8083063 Iustin Pop
def ShellQuote(value):
1062 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
1063 3ecf6786 Iustin Pop

1064 58885d79 Iustin Pop
  @type value: str
1065 58885d79 Iustin Pop
  @param value: the argument to be quoted
1066 58885d79 Iustin Pop
  @rtype: str
1067 58885d79 Iustin Pop
  @return: the quoted value
1068 58885d79 Iustin Pop

1069 a8083063 Iustin Pop
  """
1070 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
1071 a8083063 Iustin Pop
    return value
1072 a8083063 Iustin Pop
  else:
1073 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
1074 a8083063 Iustin Pop
1075 a8083063 Iustin Pop
1076 a8083063 Iustin Pop
def ShellQuoteArgs(args):
1077 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
1078 58885d79 Iustin Pop

1079 58885d79 Iustin Pop
  @type args: list
1080 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
1081 58885d79 Iustin Pop
  @rtype: str
1082 5bbd3f7f Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
1083 a8083063 Iustin Pop

1084 a8083063 Iustin Pop
  """
1085 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
1086 88d14415 Michael Hanselmann
1087 88d14415 Michael Hanselmann
1088 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1089 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
1090 2c30e9d7 Alexander Schreiber

1091 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1092 58885d79 Iustin Pop
  to it.
1093 58885d79 Iustin Pop

1094 58885d79 Iustin Pop
  @type target: str
1095 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
1096 58885d79 Iustin Pop
  @type port: int
1097 58885d79 Iustin Pop
  @param port: the port to connect to
1098 58885d79 Iustin Pop
  @type timeout: int
1099 5bbd3f7f Michael Hanselmann
  @param timeout: the timeout on the connection attempt
1100 58885d79 Iustin Pop
  @type live_port_needed: boolean
1101 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
1102 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
1103 58885d79 Iustin Pop
  @type source: str or None
1104 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
1105 58885d79 Iustin Pop
      from this specific source address; failures to bind other
1106 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
1107 2c30e9d7 Alexander Schreiber

1108 2c30e9d7 Alexander Schreiber
  """
1109 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1110 2c30e9d7 Alexander Schreiber
1111 0b5ad33e Iustin Pop
  success = False
1112 2c30e9d7 Alexander Schreiber
1113 b15d625f Iustin Pop
  if source is not None:
1114 b15d625f Iustin Pop
    try:
1115 b15d625f Iustin Pop
      sock.bind((source, 0))
1116 7c4d6c7b Michael Hanselmann
    except socket.error, (errcode, _):
1117 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
1118 b15d625f Iustin Pop
        success = False
1119 2c30e9d7 Alexander Schreiber
1120 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
1121 2c30e9d7 Alexander Schreiber
1122 2c30e9d7 Alexander Schreiber
  try:
1123 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
1124 2c30e9d7 Alexander Schreiber
    sock.close()
1125 2c30e9d7 Alexander Schreiber
    success = True
1126 2c30e9d7 Alexander Schreiber
  except socket.timeout:
1127 2c30e9d7 Alexander Schreiber
    success = False
1128 099c52ad Iustin Pop
  except socket.error, (errcode, _):
1129 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1130 2c30e9d7 Alexander Schreiber
1131 2c30e9d7 Alexander Schreiber
  return success
1132 eedbda4b Michael Hanselmann
1133 eedbda4b Michael Hanselmann
1134 caad16e2 Iustin Pop
def OwnIpAddress(address):
1135 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1136 caad16e2 Iustin Pop

1137 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1138 caad16e2 Iustin Pop
  address.
1139 caad16e2 Iustin Pop

1140 caad16e2 Iustin Pop
  @type address: string
1141 5bbd3f7f Michael Hanselmann
  @param address: the address to check
1142 caad16e2 Iustin Pop
  @rtype: bool
1143 58885d79 Iustin Pop
  @return: True if we own the address
1144 caad16e2 Iustin Pop

1145 caad16e2 Iustin Pop
  """
1146 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1147 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1148 caad16e2 Iustin Pop
1149 caad16e2 Iustin Pop
1150 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1151 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1152 58885d79 Iustin Pop

1153 58885d79 Iustin Pop
  @type path: str
1154 58885d79 Iustin Pop
  @param path: the directory to enumerate
1155 58885d79 Iustin Pop
  @rtype: list
1156 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1157 eedbda4b Michael Hanselmann

1158 eedbda4b Michael Hanselmann
  """
1159 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1160 f3299a07 Michael Hanselmann
  files.sort()
1161 f3299a07 Michael Hanselmann
  return files
1162 2f8b60b3 Iustin Pop
1163 2f8b60b3 Iustin Pop
1164 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1165 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1166 257f4c0a Iustin Pop

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

1171 2f8b60b3 Iustin Pop
  """
1172 2f8b60b3 Iustin Pop
  try:
1173 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1174 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1175 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1176 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1177 257f4c0a Iustin Pop
    else:
1178 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1179 257f4c0a Iustin Pop
                                   type(user))
1180 2f8b60b3 Iustin Pop
  except KeyError:
1181 2f8b60b3 Iustin Pop
    return default
1182 2f8b60b3 Iustin Pop
  return result.pw_dir
1183 59072e7e Michael Hanselmann
1184 59072e7e Michael Hanselmann
1185 24818e8f Michael Hanselmann
def NewUUID():
1186 59072e7e Michael Hanselmann
  """Returns a random UUID.
1187 59072e7e Michael Hanselmann

1188 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1189 58885d79 Iustin Pop
      filesystem.
1190 58885d79 Iustin Pop
  @rtype: str
1191 58885d79 Iustin Pop

1192 59072e7e Michael Hanselmann
  """
1193 13998ef2 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1194 087b34fe Iustin Pop
1195 087b34fe Iustin Pop
1196 ec2c2bc4 Luca Bigliardi
def GenerateSecret(numbytes=20):
1197 33081d90 Iustin Pop
  """Generates a random secret.
1198 33081d90 Iustin Pop

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

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

1207 33081d90 Iustin Pop
  """
1208 ec2c2bc4 Luca Bigliardi
  return os.urandom(numbytes).encode('hex')
1209 33081d90 Iustin Pop
1210 33081d90 Iustin Pop
1211 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1212 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1213 9dae41ad Guido Trotter

1214 9dae41ad Guido Trotter
  @param dirs: list of tuples (dir_name, dir_mode)
1215 9dae41ad Guido Trotter
  @type dirs: list of (string, integer)
1216 9dae41ad Guido Trotter

1217 9dae41ad Guido Trotter
  """
1218 9dae41ad Guido Trotter
  for dir_name, dir_mode in dirs:
1219 9dae41ad Guido Trotter
    try:
1220 1b2c8f85 Iustin Pop
      os.mkdir(dir_name, dir_mode)
1221 9dae41ad Guido Trotter
    except EnvironmentError, err:
1222 9dae41ad Guido Trotter
      if err.errno != errno.EEXIST:
1223 9dae41ad Guido Trotter
        raise errors.GenericError("Cannot create needed directory"
1224 1b2c8f85 Iustin Pop
                                  " '%s': %s" % (dir_name, err))
1225 9dae41ad Guido Trotter
    if not os.path.isdir(dir_name):
1226 9dae41ad Guido Trotter
      raise errors.GenericError("%s is not a directory" % dir_name)
1227 9dae41ad Guido Trotter
1228 9dae41ad Guido Trotter
1229 016308cb Iustin Pop
def ReadFile(file_name, size=-1):
1230 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1231 ca0aa6d0 Michael Hanselmann

1232 016308cb Iustin Pop
  @type size: int
1233 016308cb Iustin Pop
  @param size: Read at most size bytes (if negative, entire file)
1234 58885d79 Iustin Pop
  @rtype: str
1235 5bbd3f7f Michael Hanselmann
  @return: the (possibly partial) content of the file
1236 ca0aa6d0 Michael Hanselmann

1237 ca0aa6d0 Michael Hanselmann
  """
1238 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1239 ca0aa6d0 Michael Hanselmann
  try:
1240 016308cb Iustin Pop
    return f.read(size)
1241 ca0aa6d0 Michael Hanselmann
  finally:
1242 ca0aa6d0 Michael Hanselmann
    f.close()
1243 ca0aa6d0 Michael Hanselmann
1244 ca0aa6d0 Michael Hanselmann
1245 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1246 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1247 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1248 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1249 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1250 087b34fe Iustin Pop
  """(Over)write a file atomically.
1251 087b34fe Iustin Pop

1252 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1253 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1254 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1255 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1256 087b34fe Iustin Pop
  mtime/atime of the file.
1257 087b34fe Iustin Pop

1258 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1259 69efe319 Michael Hanselmann
  target file has the new contents. If the function has raised an
1260 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1261 087b34fe Iustin Pop
  temporary file should be removed.
1262 087b34fe Iustin Pop

1263 58885d79 Iustin Pop
  @type file_name: str
1264 58885d79 Iustin Pop
  @param file_name: the target filename
1265 58885d79 Iustin Pop
  @type fn: callable
1266 58885d79 Iustin Pop
  @param fn: content writing function, called with
1267 58885d79 Iustin Pop
      file descriptor as parameter
1268 69efe319 Michael Hanselmann
  @type data: str
1269 58885d79 Iustin Pop
  @param data: contents of the file
1270 58885d79 Iustin Pop
  @type mode: int
1271 58885d79 Iustin Pop
  @param mode: file mode
1272 58885d79 Iustin Pop
  @type uid: int
1273 58885d79 Iustin Pop
  @param uid: the owner of the file
1274 58885d79 Iustin Pop
  @type gid: int
1275 58885d79 Iustin Pop
  @param gid: the group of the file
1276 58885d79 Iustin Pop
  @type atime: int
1277 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1278 58885d79 Iustin Pop
  @type mtime: int
1279 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1280 58885d79 Iustin Pop
  @type close: boolean
1281 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1282 58885d79 Iustin Pop
  @type prewrite: callable
1283 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1284 58885d79 Iustin Pop
  @type postwrite: callable
1285 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1286 58885d79 Iustin Pop

1287 58885d79 Iustin Pop
  @rtype: None or int
1288 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1289 58885d79 Iustin Pop
      otherwise the file descriptor
1290 58885d79 Iustin Pop

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

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

1347 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1348 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1349 7b4126b7 Iustin Pop
  value, the index will be returned.
1350 7b4126b7 Iustin Pop

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

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

1356 58885d79 Iustin Pop
  @type seq: sequence
1357 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1358 58885d79 Iustin Pop
  @type base: int
1359 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1360 58885d79 Iustin Pop
  @rtype: int
1361 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1362 7b4126b7 Iustin Pop

1363 7b4126b7 Iustin Pop
  """
1364 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1365 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1366 7b4126b7 Iustin Pop
    if elem > idx + base:
1367 7b4126b7 Iustin Pop
      # idx is not used
1368 7b4126b7 Iustin Pop
      return idx + base
1369 7b4126b7 Iustin Pop
  return None
1370 7b4126b7 Iustin Pop
1371 7b4126b7 Iustin Pop
1372 7260cfbe Iustin Pop
def all(seq, pred=bool): # pylint: disable-msg=W0622
1373 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1374 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilterfalse(pred, seq):
1375 78feb6fb Guido Trotter
    return False
1376 78feb6fb Guido Trotter
  return True
1377 78feb6fb Guido Trotter
1378 78feb6fb Guido Trotter
1379 7260cfbe Iustin Pop
def any(seq, pred=bool): # pylint: disable-msg=W0622
1380 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1381 7c4d6c7b Michael Hanselmann
  for _ in itertools.ifilter(pred, seq):
1382 78feb6fb Guido Trotter
    return True
1383 78feb6fb Guido Trotter
  return False
1384 f7414041 Michael Hanselmann
1385 f7414041 Michael Hanselmann
1386 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1387 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1388 f7414041 Michael Hanselmann

1389 f7414041 Michael Hanselmann
  Element order is preserved.
1390 58885d79 Iustin Pop

1391 58885d79 Iustin Pop
  @type seq: sequence
1392 5bbd3f7f Michael Hanselmann
  @param seq: the sequence with the source elements
1393 58885d79 Iustin Pop
  @rtype: list
1394 58885d79 Iustin Pop
  @return: list of unique elements from seq
1395 58885d79 Iustin Pop

1396 f7414041 Michael Hanselmann
  """
1397 f7414041 Michael Hanselmann
  seen = set()
1398 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1399 1862d460 Alexander Schreiber
1400 1862d460 Alexander Schreiber
1401 82187135 Renรฉ Nussbaumer
def NormalizeAndValidateMac(mac):
1402 82187135 Renรฉ Nussbaumer
  """Normalizes and check if a MAC address is valid.
1403 1862d460 Alexander Schreiber

1404 5bbd3f7f Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
1405 82187135 Renรฉ Nussbaumer
  accepts colon separated format. Normalize it to all lower.
1406 58885d79 Iustin Pop

1407 58885d79 Iustin Pop
  @type mac: str
1408 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1409 82187135 Renรฉ Nussbaumer
  @rtype: str
1410 82187135 Renรฉ Nussbaumer
  @return: returns the normalized and validated MAC.
1411 82187135 Renรฉ Nussbaumer

1412 82187135 Renรฉ Nussbaumer
  @raise errors.OpPrereqError: If the MAC isn't valid
1413 58885d79 Iustin Pop

1414 1862d460 Alexander Schreiber
  """
1415 82187135 Renรฉ Nussbaumer
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1416 82187135 Renรฉ Nussbaumer
  if not mac_check.match(mac):
1417 82187135 Renรฉ Nussbaumer
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1418 82187135 Renรฉ Nussbaumer
                               mac, errors.ECODE_INVAL)
1419 82187135 Renรฉ Nussbaumer
1420 82187135 Renรฉ Nussbaumer
  return mac.lower()
1421 06009e27 Iustin Pop
1422 06009e27 Iustin Pop
1423 06009e27 Iustin Pop
def TestDelay(duration):
1424 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1425 06009e27 Iustin Pop

1426 58885d79 Iustin Pop
  @type duration: float
1427 58885d79 Iustin Pop
  @param duration: the sleep duration
1428 58885d79 Iustin Pop
  @rtype: boolean
1429 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1430 58885d79 Iustin Pop

1431 06009e27 Iustin Pop
  """
1432 06009e27 Iustin Pop
  if duration < 0:
1433 38ea42a1 Iustin Pop
    return False, "Invalid sleep duration"
1434 06009e27 Iustin Pop
  time.sleep(duration)
1435 38ea42a1 Iustin Pop
  return True, None
1436 8f765069 Iustin Pop
1437 8f765069 Iustin Pop
1438 7d88772a Iustin Pop
def _CloseFDNoErr(fd, retries=5):
1439 7d88772a Iustin Pop
  """Close a file descriptor ignoring errors.
1440 8f765069 Iustin Pop

1441 7d88772a Iustin Pop
  @type fd: int
1442 7d88772a Iustin Pop
  @param fd: the file descriptor
1443 7d88772a Iustin Pop
  @type retries: int
1444 7d88772a Iustin Pop
  @param retries: how many retries to make, in case we get any
1445 7d88772a Iustin Pop
      other error than EBADF
1446 7d88772a Iustin Pop

1447 7d88772a Iustin Pop
  """
1448 7d88772a Iustin Pop
  try:
1449 7d88772a Iustin Pop
    os.close(fd)
1450 7d88772a Iustin Pop
  except OSError, err:
1451 7d88772a Iustin Pop
    if err.errno != errno.EBADF:
1452 7d88772a Iustin Pop
      if retries > 0:
1453 7d88772a Iustin Pop
        _CloseFDNoErr(fd, retries - 1)
1454 7d88772a Iustin Pop
    # else either it's closed already or we're out of retries, so we
1455 7d88772a Iustin Pop
    # ignore this and go on
1456 7d88772a Iustin Pop
1457 7d88772a Iustin Pop
1458 7d88772a Iustin Pop
def CloseFDs(noclose_fds=None):
1459 7d88772a Iustin Pop
  """Close file descriptors.
1460 7d88772a Iustin Pop

1461 7d88772a Iustin Pop
  This closes all file descriptors above 2 (i.e. except
1462 7d88772a Iustin Pop
  stdin/out/err).
1463 8f765069 Iustin Pop

1464 58885d79 Iustin Pop
  @type noclose_fds: list or None
1465 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1466 58885d79 Iustin Pop
      that should not be closed
1467 58885d79 Iustin Pop

1468 8f765069 Iustin Pop
  """
1469 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1470 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1471 8f765069 Iustin Pop
    try:
1472 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1473 8f765069 Iustin Pop
      if MAXFD < 0:
1474 8f765069 Iustin Pop
        MAXFD = 1024
1475 8f765069 Iustin Pop
    except OSError:
1476 8f765069 Iustin Pop
      MAXFD = 1024
1477 8f765069 Iustin Pop
  else:
1478 8f765069 Iustin Pop
    MAXFD = 1024
1479 7d88772a Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1480 7d88772a Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1481 7d88772a Iustin Pop
    maxfd = MAXFD
1482 7d88772a Iustin Pop
1483 7d88772a Iustin Pop
  # Iterate through and close all file descriptors (except the standard ones)
1484 7d88772a Iustin Pop
  for fd in range(3, maxfd):
1485 7d88772a Iustin Pop
    if noclose_fds and fd in noclose_fds:
1486 7d88772a Iustin Pop
      continue
1487 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1488 7d88772a Iustin Pop
1489 7d88772a Iustin Pop
1490 7d88772a Iustin Pop
def Daemonize(logfile):
1491 7d88772a Iustin Pop
  """Daemonize the current process.
1492 7d88772a Iustin Pop

1493 7d88772a Iustin Pop
  This detaches the current process from the controlling terminal and
1494 7d88772a Iustin Pop
  runs it in the background as a daemon.
1495 7d88772a Iustin Pop

1496 7d88772a Iustin Pop
  @type logfile: str
1497 7d88772a Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1498 7d88772a Iustin Pop
  @rtype: int
1499 5fcc718f Iustin Pop
  @return: the value zero
1500 7d88772a Iustin Pop

1501 7d88772a Iustin Pop
  """
1502 7260cfbe Iustin Pop
  # pylint: disable-msg=W0212
1503 7260cfbe Iustin Pop
  # yes, we really want os._exit
1504 7d88772a Iustin Pop
  UMASK = 077
1505 7d88772a Iustin Pop
  WORKDIR = "/"
1506 8f765069 Iustin Pop
1507 8f765069 Iustin Pop
  # this might fail
1508 8f765069 Iustin Pop
  pid = os.fork()
1509 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1510 8f765069 Iustin Pop
    os.setsid()
1511 8f765069 Iustin Pop
    # this might fail
1512 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1513 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1514 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1515 8f765069 Iustin Pop
      os.umask(UMASK)
1516 8f765069 Iustin Pop
    else:
1517 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1518 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1519 8f765069 Iustin Pop
  else:
1520 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1521 8f765069 Iustin Pop
1522 7d88772a Iustin Pop
  for fd in range(3):
1523 7d88772a Iustin Pop
    _CloseFDNoErr(fd)
1524 7d88772a Iustin Pop
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1525 7d88772a Iustin Pop
  assert i == 0, "Can't close/reopen stdin"
1526 7d88772a Iustin Pop
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1527 7d88772a Iustin Pop
  assert i == 1, "Can't close/reopen stdout"
1528 7d88772a Iustin Pop
  # Duplicate standard output to standard error.
1529 7d88772a Iustin Pop
  os.dup2(1, 2)
1530 8f765069 Iustin Pop
  return 0
1531 57c177af Iustin Pop
1532 57c177af Iustin Pop
1533 53beffbb Iustin Pop
def DaemonPidFileName(name):
1534 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1535 58885d79 Iustin Pop

1536 58885d79 Iustin Pop
  @type name: str
1537 58885d79 Iustin Pop
  @param name: the daemon name
1538 58885d79 Iustin Pop
  @rtype: str
1539 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1540 58885d79 Iustin Pop
      daemon name
1541 b330ac0b Guido Trotter

1542 b330ac0b Guido Trotter
  """
1543 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1544 b330ac0b Guido Trotter
1545 b330ac0b Guido Trotter
1546 b330ac0b Guido Trotter
def WritePidFile(name):
1547 b330ac0b Guido Trotter
  """Write the current process pidfile.
1548 b330ac0b Guido Trotter

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

1551 58885d79 Iustin Pop
  @type name: str
1552 58885d79 Iustin Pop
  @param name: the daemon name to use
1553 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1554 58885d79 Iustin Pop
      points to a live process
1555 b330ac0b Guido Trotter

1556 b330ac0b Guido Trotter
  """
1557 b330ac0b Guido Trotter
  pid = os.getpid()
1558 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1559 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1560 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1561 b330ac0b Guido Trotter
1562 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1563 b330ac0b Guido Trotter
1564 b330ac0b Guido Trotter
1565 b330ac0b Guido Trotter
def RemovePidFile(name):
1566 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1567 b330ac0b Guido Trotter

1568 b330ac0b Guido Trotter
  Any errors are ignored.
1569 b330ac0b Guido Trotter

1570 58885d79 Iustin Pop
  @type name: str
1571 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1572 58885d79 Iustin Pop

1573 b330ac0b Guido Trotter
  """
1574 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1575 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1576 b330ac0b Guido Trotter
  try:
1577 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1578 7260cfbe Iustin Pop
  except: # pylint: disable-msg=W0702
1579 b330ac0b Guido Trotter
    pass
1580 b330ac0b Guido Trotter
1581 b330ac0b Guido Trotter
1582 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1583 ff5251bc Iustin Pop
                waitpid=False):
1584 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1585 b2a1f511 Iustin Pop

1586 b2a1f511 Iustin Pop
  @type pid: int
1587 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1588 38206f3c Iustin Pop
  @type signal_: int
1589 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1590 b2a1f511 Iustin Pop
  @type timeout: int
1591 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1592 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1593 b2a1f511 Iustin Pop
                  will be done
1594 ff5251bc Iustin Pop
  @type waitpid: boolean
1595 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1596 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1597 ff5251bc Iustin Pop
      would remain as zombie
1598 b2a1f511 Iustin Pop

1599 b2a1f511 Iustin Pop
  """
1600 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1601 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1602 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1603 ff5251bc Iustin Pop
    if wait:
1604 ff5251bc Iustin Pop
      try:
1605 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1606 ff5251bc Iustin Pop
      except OSError:
1607 ff5251bc Iustin Pop
        pass
1608 ff5251bc Iustin Pop
1609 b2a1f511 Iustin Pop
  if pid <= 0:
1610 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1611 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1612 b2a1f511 Iustin Pop
1613 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1614 b2a1f511 Iustin Pop
    return
1615 31892b4c Michael Hanselmann
1616 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1617 31892b4c Michael Hanselmann
1618 b2a1f511 Iustin Pop
  if timeout <= 0:
1619 b2a1f511 Iustin Pop
    return
1620 7167159a Michael Hanselmann
1621 31892b4c Michael Hanselmann
  def _CheckProcess():
1622 31892b4c Michael Hanselmann
    if not IsProcessAlive(pid):
1623 31892b4c Michael Hanselmann
      return
1624 31892b4c Michael Hanselmann
1625 7167159a Michael Hanselmann
    try:
1626 7167159a Michael Hanselmann
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1627 7167159a Michael Hanselmann
    except OSError:
1628 31892b4c Michael Hanselmann
      raise RetryAgain()
1629 31892b4c Michael Hanselmann
1630 31892b4c Michael Hanselmann
    if result_pid > 0:
1631 31892b4c Michael Hanselmann
      return
1632 31892b4c Michael Hanselmann
1633 31892b4c Michael Hanselmann
    raise RetryAgain()
1634 31892b4c Michael Hanselmann
1635 31892b4c Michael Hanselmann
  try:
1636 31892b4c Michael Hanselmann
    # Wait up to $timeout seconds
1637 31892b4c Michael Hanselmann
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1638 31892b4c Michael Hanselmann
  except RetryTimeout:
1639 31892b4c Michael Hanselmann
    pass
1640 7167159a Michael Hanselmann
1641 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1642 7167159a Michael Hanselmann
    # Kill process if it's still alive
1643 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1644 b2a1f511 Iustin Pop
1645 b2a1f511 Iustin Pop
1646 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1647 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1648 57c177af Iustin Pop

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

1652 58885d79 Iustin Pop
  @type name: str
1653 58885d79 Iustin Pop
  @param name: the name to look for
1654 58885d79 Iustin Pop
  @type search_path: str
1655 58885d79 Iustin Pop
  @param search_path: location to start at
1656 58885d79 Iustin Pop
  @type test: callable
1657 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1658 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1659 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1660 58885d79 Iustin Pop
  @rtype: str or None
1661 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1662 57c177af Iustin Pop

1663 57c177af Iustin Pop
  """
1664 f95c81bf Iustin Pop
  # validate the filename mask
1665 f95c81bf Iustin Pop
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1666 f95c81bf Iustin Pop
    logging.critical("Invalid value passed for external script name: '%s'",
1667 f95c81bf Iustin Pop
                     name)
1668 f95c81bf Iustin Pop
    return None
1669 f95c81bf Iustin Pop
1670 57c177af Iustin Pop
  for dir_name in search_path:
1671 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1672 f95c81bf Iustin Pop
    # check the user test and that we're indeed resolving to the given
1673 f95c81bf Iustin Pop
    # basename
1674 f95c81bf Iustin Pop
    if test(item_name) and os.path.basename(item_name) == name:
1675 57c177af Iustin Pop
      return item_name
1676 57c177af Iustin Pop
  return None
1677 8d1a2a64 Michael Hanselmann
1678 8d1a2a64 Michael Hanselmann
1679 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1680 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1681 8d1a2a64 Michael Hanselmann

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

1685 58885d79 Iustin Pop
  @type vglist: dict
1686 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1687 58885d79 Iustin Pop
  @type vgname: str
1688 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1689 58885d79 Iustin Pop
  @type minsize: int
1690 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1691 58885d79 Iustin Pop
  @rtype: None or str
1692 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1693 8d1a2a64 Michael Hanselmann

1694 8d1a2a64 Michael Hanselmann
  """
1695 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1696 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1697 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1698 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1699 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1700 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1701 8d1a2a64 Michael Hanselmann
  return None
1702 7996a135 Iustin Pop
1703 7996a135 Iustin Pop
1704 45bc5e4a Michael Hanselmann
def SplitTime(value):
1705 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1706 739be818 Michael Hanselmann

1707 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1708 45bc5e4a Michael Hanselmann
  @type value: int or float
1709 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1710 739be818 Michael Hanselmann

1711 739be818 Michael Hanselmann
  """
1712 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1713 45bc5e4a Michael Hanselmann
1714 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1715 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1716 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1717 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1718 45bc5e4a Michael Hanselmann
1719 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1720 739be818 Michael Hanselmann
1721 739be818 Michael Hanselmann
1722 739be818 Michael Hanselmann
def MergeTime(timetuple):
1723 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1724 739be818 Michael Hanselmann

1725 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1726 739be818 Michael Hanselmann
  @type timetuple: tuple
1727 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1728 739be818 Michael Hanselmann

1729 739be818 Michael Hanselmann
  """
1730 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1731 739be818 Michael Hanselmann
1732 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1733 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1734 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1735 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1736 739be818 Michael Hanselmann
1737 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1738 739be818 Michael Hanselmann
1739 739be818 Michael Hanselmann
1740 cd50653c Guido Trotter
def GetDaemonPort(daemon_name):
1741 cd50653c Guido Trotter
  """Get the daemon port for this cluster.
1742 4a8b186a Michael Hanselmann

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

1747 cd50653c Guido Trotter
  @type daemon_name: string
1748 cd50653c Guido Trotter
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1749 58885d79 Iustin Pop
  @rtype: int
1750 58885d79 Iustin Pop

1751 4a8b186a Michael Hanselmann
  """
1752 cd50653c Guido Trotter
  if daemon_name not in constants.DAEMONS_PORTS:
1753 cd50653c Guido Trotter
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1754 cd50653c Guido Trotter
1755 cd50653c Guido Trotter
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1756 4a8b186a Michael Hanselmann
  try:
1757 cd50653c Guido Trotter
    port = socket.getservbyname(daemon_name, proto)
1758 4a8b186a Michael Hanselmann
  except socket.error:
1759 cd50653c Guido Trotter
    port = default_port
1760 4a8b186a Michael Hanselmann
1761 4a8b186a Michael Hanselmann
  return port
1762 4a8b186a Michael Hanselmann
1763 4a8b186a Michael Hanselmann
1764 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1765 d21d09d6 Iustin Pop
                 multithreaded=False):
1766 82d9caef Iustin Pop
  """Configures the logging module.
1767 82d9caef Iustin Pop

1768 58885d79 Iustin Pop
  @type logfile: str
1769 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1770 58885d79 Iustin Pop
  @type debug: boolean
1771 58885d79 Iustin Pop
  @param debug: whether to enable debug messages too or
1772 58885d79 Iustin Pop
      only those at C{INFO} and above level
1773 58885d79 Iustin Pop
  @type stderr_logging: boolean
1774 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1775 58885d79 Iustin Pop
  @type program: str
1776 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1777 d21d09d6 Iustin Pop
  @type multithreaded: boolean
1778 d21d09d6 Iustin Pop
  @param multithreaded: if True, will add the thread name to the log file
1779 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1780 58885d79 Iustin Pop
      stderr logging is disabled
1781 58885d79 Iustin Pop

1782 82d9caef Iustin Pop
  """
1783 d21d09d6 Iustin Pop
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
1784 d21d09d6 Iustin Pop
  if multithreaded:
1785 d21d09d6 Iustin Pop
    fmt += "/%(threadName)s"
1786 82d9caef Iustin Pop
  if debug:
1787 d21d09d6 Iustin Pop
    fmt += " %(module)s:%(lineno)s"
1788 d21d09d6 Iustin Pop
  fmt += " %(levelname)s %(message)s"
1789 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1790 82d9caef Iustin Pop
1791 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1792 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1793 82d9caef Iustin Pop
1794 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1795 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1796 7d88772a Iustin Pop
    handler.close()
1797 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1798 6346a9e5 Michael Hanselmann
1799 82d9caef Iustin Pop
  if stderr_logging:
1800 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1801 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1802 82d9caef Iustin Pop
    if debug:
1803 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1804 82d9caef Iustin Pop
    else:
1805 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1806 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1807 82d9caef Iustin Pop
1808 82d9caef Iustin Pop
  # this can fail, if the logging directories are not setup or we have
1809 82d9caef Iustin Pop
  # a permisssion problem; in this case, it's best to log but ignore
1810 82d9caef Iustin Pop
  # the error if stderr_logging is True, and if false we re-raise the
1811 82d9caef Iustin Pop
  # exception since otherwise we could run but without any logs at all
1812 82d9caef Iustin Pop
  try:
1813 82d9caef Iustin Pop
    logfile_handler = logging.FileHandler(logfile)
1814 82d9caef Iustin Pop
    logfile_handler.setFormatter(formatter)
1815 82d9caef Iustin Pop
    if debug:
1816 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.DEBUG)
1817 82d9caef Iustin Pop
    else:
1818 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.INFO)
1819 82d9caef Iustin Pop
    root_logger.addHandler(logfile_handler)
1820 d21d09d6 Iustin Pop
  except EnvironmentError:
1821 82d9caef Iustin Pop
    if stderr_logging:
1822 82d9caef Iustin Pop
      logging.exception("Failed to enable logging to file '%s'", logfile)
1823 82d9caef Iustin Pop
    else:
1824 82d9caef Iustin Pop
      # we need to re-raise the exception
1825 82d9caef Iustin Pop
      raise
1826 82d9caef Iustin Pop
1827 016d04b3 Michael Hanselmann
1828 da961187 Guido Trotter
def IsNormAbsPath(path):
1829 17c61836 Guido Trotter
  """Check whether a path is absolute and also normalized
1830 da961187 Guido Trotter

1831 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1832 da961187 Guido Trotter

1833 da961187 Guido Trotter
  """
1834 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1835 82d9caef Iustin Pop
1836 016d04b3 Michael Hanselmann
1837 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1838 f65f63ef Iustin Pop
  """Return the last lines from a file.
1839 f65f63ef Iustin Pop

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

1844 f65f63ef Iustin Pop
  @param fname: the file name
1845 f65f63ef Iustin Pop
  @type lines: int
1846 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1847 f65f63ef Iustin Pop

1848 f65f63ef Iustin Pop
  """
1849 f65f63ef Iustin Pop
  fd = open(fname, "r")
1850 f65f63ef Iustin Pop
  try:
1851 f65f63ef Iustin Pop
    fd.seek(0, 2)
1852 f65f63ef Iustin Pop
    pos = fd.tell()
1853 f65f63ef Iustin Pop
    pos = max(0, pos-4096)
1854 f65f63ef Iustin Pop
    fd.seek(pos, 0)
1855 f65f63ef Iustin Pop
    raw_data = fd.read()
1856 f65f63ef Iustin Pop
  finally:
1857 f65f63ef Iustin Pop
    fd.close()
1858 f65f63ef Iustin Pop
1859 f65f63ef Iustin Pop
  rows = raw_data.splitlines()
1860 f65f63ef Iustin Pop
  return rows[-lines:]
1861 f65f63ef Iustin Pop
1862 f65f63ef Iustin Pop
1863 26f15862 Iustin Pop
def SafeEncode(text):
1864 26f15862 Iustin Pop
  """Return a 'safe' version of a source string.
1865 26f15862 Iustin Pop

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

1875 26f15862 Iustin Pop
  @type text: str or unicode
1876 26f15862 Iustin Pop
  @param text: input data
1877 26f15862 Iustin Pop
  @rtype: str
1878 26f15862 Iustin Pop
  @return: a safe version of text
1879 26f15862 Iustin Pop

1880 26f15862 Iustin Pop
  """
1881 d392fa34 Iustin Pop
  if isinstance(text, unicode):
1882 5bbd3f7f Michael Hanselmann
    # only if unicode; if str already, we handle it below
1883 d392fa34 Iustin Pop
    text = text.encode('ascii', 'backslashreplace')
1884 d392fa34 Iustin Pop
  resu = ""
1885 d392fa34 Iustin Pop
  for char in text:
1886 d392fa34 Iustin Pop
    c = ord(char)
1887 d392fa34 Iustin Pop
    if char  == '\t':
1888 d392fa34 Iustin Pop
      resu += r'\t'
1889 d392fa34 Iustin Pop
    elif char == '\n':
1890 d392fa34 Iustin Pop
      resu += r'\n'
1891 d392fa34 Iustin Pop
    elif char == '\r':
1892 d392fa34 Iustin Pop
      resu += r'\'r'
1893 d392fa34 Iustin Pop
    elif c < 32 or c >= 127: # non-printable
1894 d392fa34 Iustin Pop
      resu += "\\x%02x" % (c & 0xff)
1895 d392fa34 Iustin Pop
    else:
1896 d392fa34 Iustin Pop
      resu += char
1897 d392fa34 Iustin Pop
  return resu
1898 26f15862 Iustin Pop
1899 26f15862 Iustin Pop
1900 5b69bc7c Iustin Pop
def UnescapeAndSplit(text, sep=","):
1901 5b69bc7c Iustin Pop
  """Split and unescape a string based on a given separator.
1902 5b69bc7c Iustin Pop

1903 5b69bc7c Iustin Pop
  This function splits a string based on a separator where the
1904 5b69bc7c Iustin Pop
  separator itself can be escape in order to be an element of the
1905 5b69bc7c Iustin Pop
  elements. The escaping rules are (assuming coma being the
1906 5b69bc7c Iustin Pop
  separator):
1907 5b69bc7c Iustin Pop
    - a plain , separates the elements
1908 5b69bc7c Iustin Pop
    - a sequence \\\\, (double backslash plus comma) is handled as a
1909 5b69bc7c Iustin Pop
      backslash plus a separator comma
1910 5b69bc7c Iustin Pop
    - a sequence \, (backslash plus comma) is handled as a
1911 5b69bc7c Iustin Pop
      non-separator comma
1912 5b69bc7c Iustin Pop

1913 5b69bc7c Iustin Pop
  @type text: string
1914 5b69bc7c Iustin Pop
  @param text: the string to split
1915 5b69bc7c Iustin Pop
  @type sep: string
1916 5b69bc7c Iustin Pop
  @param text: the separator
1917 5b69bc7c Iustin Pop
  @rtype: string
1918 5b69bc7c Iustin Pop
  @return: a list of strings
1919 5b69bc7c Iustin Pop

1920 5b69bc7c Iustin Pop
  """
1921 5b69bc7c Iustin Pop
  # we split the list by sep (with no escaping at this stage)
1922 5b69bc7c Iustin Pop
  slist = text.split(sep)
1923 5b69bc7c Iustin Pop
  # next, we revisit the elements and if any of them ended with an odd
1924 5b69bc7c Iustin Pop
  # number of backslashes, then we join it with the next
1925 5b69bc7c Iustin Pop
  rlist = []
1926 5b69bc7c Iustin Pop
  while slist:
1927 5b69bc7c Iustin Pop
    e1 = slist.pop(0)
1928 5b69bc7c Iustin Pop
    if e1.endswith("\\"):
1929 5b69bc7c Iustin Pop
      num_b = len(e1) - len(e1.rstrip("\\"))
1930 5b69bc7c Iustin Pop
      if num_b % 2 == 1:
1931 5b69bc7c Iustin Pop
        e2 = slist.pop(0)
1932 5b69bc7c Iustin Pop
        # here the backslashes remain (all), and will be reduced in
1933 5b69bc7c Iustin Pop
        # the next step
1934 5b69bc7c Iustin Pop
        rlist.append(e1 + sep + e2)
1935 5b69bc7c Iustin Pop
        continue
1936 5b69bc7c Iustin Pop
    rlist.append(e1)
1937 5b69bc7c Iustin Pop
  # finally, replace backslash-something with something
1938 5b69bc7c Iustin Pop
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
1939 5b69bc7c Iustin Pop
  return rlist
1940 5b69bc7c Iustin Pop
1941 5b69bc7c Iustin Pop
1942 ab3e6da8 Iustin Pop
def CommaJoin(names):
1943 ab3e6da8 Iustin Pop
  """Nicely join a set of identifiers.
1944 ab3e6da8 Iustin Pop

1945 ab3e6da8 Iustin Pop
  @param names: set, list or tuple
1946 ab3e6da8 Iustin Pop
  @return: a string with the formatted results
1947 ab3e6da8 Iustin Pop

1948 ab3e6da8 Iustin Pop
  """
1949 1f864b60 Iustin Pop
  return ", ".join([str(val) for val in names])
1950 ab3e6da8 Iustin Pop
1951 ab3e6da8 Iustin Pop
1952 3f6a47a8 Michael Hanselmann
def BytesToMebibyte(value):
1953 3f6a47a8 Michael Hanselmann
  """Converts bytes to mebibytes.
1954 3f6a47a8 Michael Hanselmann

1955 3f6a47a8 Michael Hanselmann
  @type value: int
1956 3f6a47a8 Michael Hanselmann
  @param value: Value in bytes
1957 3f6a47a8 Michael Hanselmann
  @rtype: int
1958 3f6a47a8 Michael Hanselmann
  @return: Value in mebibytes
1959 3f6a47a8 Michael Hanselmann

1960 3f6a47a8 Michael Hanselmann
  """
1961 3f6a47a8 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
1962 3f6a47a8 Michael Hanselmann
1963 3f6a47a8 Michael Hanselmann
1964 3f6a47a8 Michael Hanselmann
def CalculateDirectorySize(path):
1965 3f6a47a8 Michael Hanselmann
  """Calculates the size of a directory recursively.
1966 3f6a47a8 Michael Hanselmann

1967 3f6a47a8 Michael Hanselmann
  @type path: string
1968 3f6a47a8 Michael Hanselmann
  @param path: Path to directory
1969 3f6a47a8 Michael Hanselmann
  @rtype: int
1970 3f6a47a8 Michael Hanselmann
  @return: Size in mebibytes
1971 3f6a47a8 Michael Hanselmann

1972 3f6a47a8 Michael Hanselmann
  """
1973 3f6a47a8 Michael Hanselmann
  size = 0
1974 3f6a47a8 Michael Hanselmann
1975 3f6a47a8 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
1976 2a887df9 Michael Hanselmann
    for filename in files:
1977 2a887df9 Michael Hanselmann
      st = os.lstat(os.path.join(curpath, filename))
1978 3f6a47a8 Michael Hanselmann
      size += st.st_size
1979 3f6a47a8 Michael Hanselmann
1980 3f6a47a8 Michael Hanselmann
  return BytesToMebibyte(size)
1981 3f6a47a8 Michael Hanselmann
1982 3f6a47a8 Michael Hanselmann
1983 620a85fd Iustin Pop
def GetFilesystemStats(path):
1984 620a85fd Iustin Pop
  """Returns the total and free space on a filesystem.
1985 3f6a47a8 Michael Hanselmann

1986 3f6a47a8 Michael Hanselmann
  @type path: string
1987 3f6a47a8 Michael Hanselmann
  @param path: Path on filesystem to be examined
1988 3f6a47a8 Michael Hanselmann
  @rtype: int
1989 620a85fd Iustin Pop
  @return: tuple of (Total space, Free space) in mebibytes
1990 3f6a47a8 Michael Hanselmann

1991 3f6a47a8 Michael Hanselmann
  """
1992 3f6a47a8 Michael Hanselmann
  st = os.statvfs(path)
1993 3f6a47a8 Michael Hanselmann
1994 620a85fd Iustin Pop
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
1995 620a85fd Iustin Pop
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
1996 620a85fd Iustin Pop
  return (tsize, fsize)
1997 3f6a47a8 Michael Hanselmann
1998 3f6a47a8 Michael Hanselmann
1999 7996a135 Iustin Pop
def LockedMethod(fn):
2000 7996a135 Iustin Pop
  """Synchronized object access decorator.
2001 7996a135 Iustin Pop

2002 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
2003 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
2004 7996a135 Iustin Pop

2005 7996a135 Iustin Pop
  """
2006 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
2007 e67bd559 Michael Hanselmann
    if debug_locks:
2008 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
2009 e67bd559 Michael Hanselmann
2010 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
2011 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
2012 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
2013 7996a135 Iustin Pop
    lock = self._lock
2014 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
2015 7996a135 Iustin Pop
    lock.acquire()
2016 7996a135 Iustin Pop
    try:
2017 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
2018 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
2019 7996a135 Iustin Pop
    finally:
2020 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
2021 7996a135 Iustin Pop
      lock.release()
2022 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
2023 7996a135 Iustin Pop
    return result
2024 7996a135 Iustin Pop
  return wrapper
2025 eb0f0ce0 Michael Hanselmann
2026 eb0f0ce0 Michael Hanselmann
2027 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
2028 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
2029 eb0f0ce0 Michael Hanselmann

2030 58885d79 Iustin Pop
  @type fd: int
2031 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
2032 58885d79 Iustin Pop

2033 eb0f0ce0 Michael Hanselmann
  """
2034 eb0f0ce0 Michael Hanselmann
  try:
2035 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2036 eb0f0ce0 Michael Hanselmann
  except IOError, err:
2037 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
2038 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
2039 eb0f0ce0 Michael Hanselmann
    raise
2040 de499029 Michael Hanselmann
2041 de499029 Michael Hanselmann
2042 3b813dd2 Iustin Pop
def FormatTime(val):
2043 3b813dd2 Iustin Pop
  """Formats a time value.
2044 3b813dd2 Iustin Pop

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

2049 3b813dd2 Iustin Pop
  """
2050 3b813dd2 Iustin Pop
  if val is None or not isinstance(val, (int, float)):
2051 3b813dd2 Iustin Pop
    return "N/A"
2052 3b813dd2 Iustin Pop
  # these two codes works on Linux, but they are not guaranteed on all
2053 3b813dd2 Iustin Pop
  # platforms
2054 3b813dd2 Iustin Pop
  return time.strftime("%F %T", time.localtime(val))
2055 3b813dd2 Iustin Pop
2056 3b813dd2 Iustin Pop
2057 5cbe43a5 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2058 05e50653 Michael Hanselmann
  """Reads the watcher pause file.
2059 05e50653 Michael Hanselmann

2060 5cbe43a5 Michael Hanselmann
  @type filename: string
2061 5cbe43a5 Michael Hanselmann
  @param filename: Path to watcher pause file
2062 5cbe43a5 Michael Hanselmann
  @type now: None, float or int
2063 5cbe43a5 Michael Hanselmann
  @param now: Current time as Unix timestamp
2064 5cbe43a5 Michael Hanselmann
  @type remove_after: int
2065 5cbe43a5 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
2066 5cbe43a5 Michael Hanselmann
    seconds past the pause end time
2067 5cbe43a5 Michael Hanselmann

2068 05e50653 Michael Hanselmann
  """
2069 05e50653 Michael Hanselmann
  if now is None:
2070 05e50653 Michael Hanselmann
    now = time.time()
2071 05e50653 Michael Hanselmann
2072 05e50653 Michael Hanselmann
  try:
2073 05e50653 Michael Hanselmann
    value = ReadFile(filename)
2074 05e50653 Michael Hanselmann
  except IOError, err:
2075 05e50653 Michael Hanselmann
    if err.errno != errno.ENOENT:
2076 05e50653 Michael Hanselmann
      raise
2077 05e50653 Michael Hanselmann
    value = None
2078 05e50653 Michael Hanselmann
2079 05e50653 Michael Hanselmann
  if value is not None:
2080 05e50653 Michael Hanselmann
    try:
2081 05e50653 Michael Hanselmann
      value = int(value)
2082 05e50653 Michael Hanselmann
    except ValueError:
2083 5cbe43a5 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2084 5cbe43a5 Michael Hanselmann
                       " removing it"), filename)
2085 5cbe43a5 Michael Hanselmann
      RemoveFile(filename)
2086 05e50653 Michael Hanselmann
      value = None
2087 05e50653 Michael Hanselmann
2088 05e50653 Michael Hanselmann
    if value is not None:
2089 5cbe43a5 Michael Hanselmann
      # Remove file if it's outdated
2090 5cbe43a5 Michael Hanselmann
      if now > (value + remove_after):
2091 5cbe43a5 Michael Hanselmann
        RemoveFile(filename)
2092 5cbe43a5 Michael Hanselmann
        value = None
2093 5cbe43a5 Michael Hanselmann
2094 5cbe43a5 Michael Hanselmann
      elif now > value:
2095 05e50653 Michael Hanselmann
        value = None
2096 05e50653 Michael Hanselmann
2097 05e50653 Michael Hanselmann
  return value
2098 05e50653 Michael Hanselmann
2099 05e50653 Michael Hanselmann
2100 de0ea66b Michael Hanselmann
class RetryTimeout(Exception):
2101 de0ea66b Michael Hanselmann
  """Retry loop timed out.
2102 de0ea66b Michael Hanselmann

2103 de0ea66b Michael Hanselmann
  """
2104 de0ea66b Michael Hanselmann
2105 de0ea66b Michael Hanselmann
2106 de0ea66b Michael Hanselmann
class RetryAgain(Exception):
2107 de0ea66b Michael Hanselmann
  """Retry again.
2108 de0ea66b Michael Hanselmann

2109 de0ea66b Michael Hanselmann
  """
2110 de0ea66b Michael Hanselmann
2111 de0ea66b Michael Hanselmann
2112 de0ea66b Michael Hanselmann
class _RetryDelayCalculator(object):
2113 de0ea66b Michael Hanselmann
  """Calculator for increasing delays.
2114 de0ea66b Michael Hanselmann

2115 de0ea66b Michael Hanselmann
  """
2116 de0ea66b Michael Hanselmann
  __slots__ = [
2117 de0ea66b Michael Hanselmann
    "_factor",
2118 de0ea66b Michael Hanselmann
    "_limit",
2119 de0ea66b Michael Hanselmann
    "_next",
2120 de0ea66b Michael Hanselmann
    "_start",
2121 de0ea66b Michael Hanselmann
    ]
2122 de0ea66b Michael Hanselmann
2123 de0ea66b Michael Hanselmann
  def __init__(self, start, factor, limit):
2124 de0ea66b Michael Hanselmann
    """Initializes this class.
2125 de0ea66b Michael Hanselmann

2126 de0ea66b Michael Hanselmann
    @type start: float
2127 de0ea66b Michael Hanselmann
    @param start: Initial delay
2128 de0ea66b Michael Hanselmann
    @type factor: float
2129 de0ea66b Michael Hanselmann
    @param factor: Factor for delay increase
2130 de0ea66b Michael Hanselmann
    @type limit: float or None
2131 de0ea66b Michael Hanselmann
    @param limit: Upper limit for delay or None for no limit
2132 de0ea66b Michael Hanselmann

2133 de0ea66b Michael Hanselmann
    """
2134 de0ea66b Michael Hanselmann
    assert start > 0.0
2135 de0ea66b Michael Hanselmann
    assert factor >= 1.0
2136 de0ea66b Michael Hanselmann
    assert limit is None or limit >= 0.0
2137 de0ea66b Michael Hanselmann
2138 de0ea66b Michael Hanselmann
    self._start = start
2139 de0ea66b Michael Hanselmann
    self._factor = factor
2140 de0ea66b Michael Hanselmann
    self._limit = limit
2141 de0ea66b Michael Hanselmann
2142 de0ea66b Michael Hanselmann
    self._next = start
2143 de0ea66b Michael Hanselmann
2144 de0ea66b Michael Hanselmann
  def __call__(self):
2145 de0ea66b Michael Hanselmann
    """Returns current delay and calculates the next one.
2146 de0ea66b Michael Hanselmann

2147 de0ea66b Michael Hanselmann
    """
2148 de0ea66b Michael Hanselmann
    current = self._next
2149 de0ea66b Michael Hanselmann
2150 de0ea66b Michael Hanselmann
    # Update for next run
2151 de0ea66b Michael Hanselmann
    if self._limit is None or self._next < self._limit:
2152 de0ea66b Michael Hanselmann
      self._next = max(self._limit, self._next * self._factor)
2153 de0ea66b Michael Hanselmann
2154 de0ea66b Michael Hanselmann
    return current
2155 de0ea66b Michael Hanselmann
2156 de0ea66b Michael Hanselmann
2157 de0ea66b Michael Hanselmann
#: Special delay to specify whole remaining timeout
2158 de0ea66b Michael Hanselmann
RETRY_REMAINING_TIME = object()
2159 de0ea66b Michael Hanselmann
2160 de0ea66b Michael Hanselmann
2161 de0ea66b Michael Hanselmann
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2162 de0ea66b Michael Hanselmann
          _time_fn=time.time):
2163 de0ea66b Michael Hanselmann
  """Call a function repeatedly until it succeeds.
2164 de0ea66b Michael Hanselmann

2165 de0ea66b Michael Hanselmann
  The function C{fn} is called repeatedly until it doesn't throw L{RetryAgain}
2166 de0ea66b Michael Hanselmann
  anymore. Between calls a delay, specified by C{delay}, is inserted. After a
2167 de0ea66b Michael Hanselmann
  total of C{timeout} seconds, this function throws L{RetryTimeout}.
2168 de0ea66b Michael Hanselmann

2169 de0ea66b Michael Hanselmann
  C{delay} can be one of the following:
2170 de0ea66b Michael Hanselmann
    - callable returning the delay length as a float
2171 de0ea66b Michael Hanselmann
    - Tuple of (start, factor, limit)
2172 de0ea66b Michael Hanselmann
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2173 de0ea66b Michael Hanselmann
      useful when overriding L{wait_fn} to wait for an external event)
2174 de0ea66b Michael Hanselmann
    - A static delay as a number (int or float)
2175 de0ea66b Michael Hanselmann

2176 de0ea66b Michael Hanselmann
  @type fn: callable
2177 de0ea66b Michael Hanselmann
  @param fn: Function to be called
2178 de0ea66b Michael Hanselmann
  @param delay: Either a callable (returning the delay), a tuple of (start,
2179 de0ea66b Michael Hanselmann
                factor, limit) (see L{_RetryDelayCalculator}),
2180 de0ea66b Michael Hanselmann
                L{RETRY_REMAINING_TIME} or a number (int or float)
2181 de0ea66b Michael Hanselmann
  @type timeout: float
2182 de0ea66b Michael Hanselmann
  @param timeout: Total timeout
2183 de0ea66b Michael Hanselmann
  @type wait_fn: callable
2184 de0ea66b Michael Hanselmann
  @param wait_fn: Waiting function
2185 de0ea66b Michael Hanselmann
  @return: Return value of function
2186 de0ea66b Michael Hanselmann

2187 de0ea66b Michael Hanselmann
  """
2188 de0ea66b Michael Hanselmann
  assert callable(fn)
2189 de0ea66b Michael Hanselmann
  assert callable(wait_fn)
2190 de0ea66b Michael Hanselmann
  assert callable(_time_fn)
2191 de0ea66b Michael Hanselmann
2192 de0ea66b Michael Hanselmann
  if args is None:
2193 de0ea66b Michael Hanselmann
    args = []
2194 de0ea66b Michael Hanselmann
2195 de0ea66b Michael Hanselmann
  end_time = _time_fn() + timeout
2196 de0ea66b Michael Hanselmann
2197 de0ea66b Michael Hanselmann
  if callable(delay):
2198 de0ea66b Michael Hanselmann
    # External function to calculate delay
2199 de0ea66b Michael Hanselmann
    calc_delay = delay
2200 de0ea66b Michael Hanselmann
2201 de0ea66b Michael Hanselmann
  elif isinstance(delay, (tuple, list)):
2202 de0ea66b Michael Hanselmann
    # Increasing delay with optional upper boundary
2203 de0ea66b Michael Hanselmann
    (start, factor, limit) = delay
2204 de0ea66b Michael Hanselmann
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2205 de0ea66b Michael Hanselmann
2206 de0ea66b Michael Hanselmann
  elif delay is RETRY_REMAINING_TIME:
2207 de0ea66b Michael Hanselmann
    # Always use the remaining time
2208 de0ea66b Michael Hanselmann
    calc_delay = None
2209 de0ea66b Michael Hanselmann
2210 de0ea66b Michael Hanselmann
  else:
2211 de0ea66b Michael Hanselmann
    # Static delay
2212 de0ea66b Michael Hanselmann
    calc_delay = lambda: delay
2213 de0ea66b Michael Hanselmann
2214 de0ea66b Michael Hanselmann
  assert calc_delay is None or callable(calc_delay)
2215 de0ea66b Michael Hanselmann
2216 de0ea66b Michael Hanselmann
  while True:
2217 de0ea66b Michael Hanselmann
    try:
2218 7260cfbe Iustin Pop
      # pylint: disable-msg=W0142
2219 de0ea66b Michael Hanselmann
      return fn(*args)
2220 de0ea66b Michael Hanselmann
    except RetryAgain:
2221 de0ea66b Michael Hanselmann
      pass
2222 de0ea66b Michael Hanselmann
2223 de0ea66b Michael Hanselmann
    remaining_time = end_time - _time_fn()
2224 de0ea66b Michael Hanselmann
2225 de0ea66b Michael Hanselmann
    if remaining_time < 0.0:
2226 de0ea66b Michael Hanselmann
      raise RetryTimeout()
2227 de0ea66b Michael Hanselmann
2228 de0ea66b Michael Hanselmann
    assert remaining_time >= 0.0
2229 de0ea66b Michael Hanselmann
2230 de0ea66b Michael Hanselmann
    if calc_delay is None:
2231 de0ea66b Michael Hanselmann
      wait_fn(remaining_time)
2232 de0ea66b Michael Hanselmann
    else:
2233 de0ea66b Michael Hanselmann
      current_delay = calc_delay()
2234 de0ea66b Michael Hanselmann
      if current_delay > 0.0:
2235 de0ea66b Michael Hanselmann
        wait_fn(current_delay)
2236 de0ea66b Michael Hanselmann
2237 de0ea66b Michael Hanselmann
2238 a87b4824 Michael Hanselmann
class FileLock(object):
2239 a87b4824 Michael Hanselmann
  """Utility class for file locks.
2240 a87b4824 Michael Hanselmann

2241 a87b4824 Michael Hanselmann
  """
2242 a87b4824 Michael Hanselmann
  def __init__(self, filename):
2243 58885d79 Iustin Pop
    """Constructor for FileLock.
2244 58885d79 Iustin Pop

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

2247 58885d79 Iustin Pop
    @type filename: str
2248 58885d79 Iustin Pop
    @param filename: path to the file to be locked
2249 58885d79 Iustin Pop

2250 58885d79 Iustin Pop
    """
2251 a87b4824 Michael Hanselmann
    self.filename = filename
2252 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
2253 a87b4824 Michael Hanselmann
2254 a87b4824 Michael Hanselmann
  def __del__(self):
2255 a87b4824 Michael Hanselmann
    self.Close()
2256 a87b4824 Michael Hanselmann
2257 a87b4824 Michael Hanselmann
  def Close(self):
2258 58885d79 Iustin Pop
    """Close the file and release the lock.
2259 58885d79 Iustin Pop

2260 58885d79 Iustin Pop
    """
2261 a87b4824 Michael Hanselmann
    if self.fd:
2262 a87b4824 Michael Hanselmann
      self.fd.close()
2263 a87b4824 Michael Hanselmann
      self.fd = None
2264 a87b4824 Michael Hanselmann
2265 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
2266 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
2267 aa74b828 Michael Hanselmann

2268 aa74b828 Michael Hanselmann
    @type flag: int
2269 58885d79 Iustin Pop
    @param flag: operation flag
2270 aa74b828 Michael Hanselmann
    @type blocking: bool
2271 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
2272 aa74b828 Michael Hanselmann
    @type timeout: None or float
2273 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
2274 aa74b828 Michael Hanselmann
                    non-blocking mode).
2275 aa74b828 Michael Hanselmann
    @type errmsg: string
2276 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
2277 aa74b828 Michael Hanselmann

2278 aa74b828 Michael Hanselmann
    """
2279 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
2280 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
2281 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
2282 a87b4824 Michael Hanselmann
2283 aa74b828 Michael Hanselmann
    if timeout is not None:
2284 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2285 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
2286 a87b4824 Michael Hanselmann
2287 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
2288 aa74b828 Michael Hanselmann
    elif not blocking:
2289 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
2290 aa74b828 Michael Hanselmann
      timeout_end = None
2291 aa74b828 Michael Hanselmann
2292 31892b4c Michael Hanselmann
    # TODO: Convert to utils.Retry
2293 31892b4c Michael Hanselmann
2294 aa74b828 Michael Hanselmann
    retry = True
2295 aa74b828 Michael Hanselmann
    while retry:
2296 aa74b828 Michael Hanselmann
      try:
2297 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
2298 aa74b828 Michael Hanselmann
        retry = False
2299 aa74b828 Michael Hanselmann
      except IOError, err:
2300 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
2301 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
2302 aa74b828 Michael Hanselmann
            # Wait before trying again
2303 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
2304 aa74b828 Michael Hanselmann
          else:
2305 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
2306 aa74b828 Michael Hanselmann
        else:
2307 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
2308 aa74b828 Michael Hanselmann
          raise
2309 aa74b828 Michael Hanselmann
2310 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
2311 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
2312 a87b4824 Michael Hanselmann

2313 58885d79 Iustin Pop
    @type blocking: boolean
2314 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2315 58885d79 Iustin Pop
        can lock the file or return immediately
2316 58885d79 Iustin Pop
    @type timeout: int or None
2317 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2318 58885d79 Iustin Pop
        (in blocking mode)
2319 58885d79 Iustin Pop

2320 a87b4824 Michael Hanselmann
    """
2321 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2322 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
2323 a87b4824 Michael Hanselmann
2324 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
2325 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
2326 a87b4824 Michael Hanselmann

2327 58885d79 Iustin Pop
    @type blocking: boolean
2328 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2329 58885d79 Iustin Pop
        can lock the file or return immediately
2330 58885d79 Iustin Pop
    @type timeout: int or None
2331 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2332 58885d79 Iustin Pop
        (in blocking mode)
2333 58885d79 Iustin Pop

2334 a87b4824 Michael Hanselmann
    """
2335 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2336 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
2337 a87b4824 Michael Hanselmann
2338 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
2339 a87b4824 Michael Hanselmann
    """Unlocks the file.
2340 a87b4824 Michael Hanselmann

2341 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
2342 58885d79 Iustin Pop
    operation::
2343 58885d79 Iustin Pop

2344 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
2345 58885d79 Iustin Pop
      operations.
2346 58885d79 Iustin Pop

2347 58885d79 Iustin Pop
    @type blocking: boolean
2348 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
2349 58885d79 Iustin Pop
        can lock the file or return immediately
2350 58885d79 Iustin Pop
    @type timeout: int or None
2351 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
2352 58885d79 Iustin Pop
        (in blocking mode)
2353 a87b4824 Michael Hanselmann

2354 a87b4824 Michael Hanselmann
    """
2355 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2356 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2357 a87b4824 Michael Hanselmann
2358 a87b4824 Michael Hanselmann
2359 451575de Guido Trotter
def SignalHandled(signums):
2360 451575de Guido Trotter
  """Signal Handled decoration.
2361 451575de Guido Trotter

2362 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
2363 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
2364 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
2365 451575de Guido Trotter
  objects as values.
2366 451575de Guido Trotter

2367 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
2368 451575de Guido Trotter
  with different handlers.
2369 451575de Guido Trotter

2370 451575de Guido Trotter
  @type signums: list
2371 451575de Guido Trotter
  @param signums: signals to intercept
2372 451575de Guido Trotter

2373 451575de Guido Trotter
  """
2374 451575de Guido Trotter
  def wrap(fn):
2375 451575de Guido Trotter
    def sig_function(*args, **kwargs):
2376 451575de Guido Trotter
      assert 'signal_handlers' not in kwargs or \
2377 451575de Guido Trotter
             kwargs['signal_handlers'] is None or \
2378 451575de Guido Trotter
             isinstance(kwargs['signal_handlers'], dict), \
2379 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
2380 451575de Guido Trotter
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2381 451575de Guido Trotter
        signal_handlers = kwargs['signal_handlers']
2382 451575de Guido Trotter
      else:
2383 451575de Guido Trotter
        signal_handlers = {}
2384 451575de Guido Trotter
        kwargs['signal_handlers'] = signal_handlers
2385 451575de Guido Trotter
      sighandler = SignalHandler(signums)
2386 451575de Guido Trotter
      try:
2387 451575de Guido Trotter
        for sig in signums:
2388 451575de Guido Trotter
          signal_handlers[sig] = sighandler
2389 451575de Guido Trotter
        return fn(*args, **kwargs)
2390 451575de Guido Trotter
      finally:
2391 451575de Guido Trotter
        sighandler.Reset()
2392 451575de Guido Trotter
    return sig_function
2393 451575de Guido Trotter
  return wrap
2394 451575de Guido Trotter
2395 451575de Guido Trotter
2396 de499029 Michael Hanselmann
class SignalHandler(object):
2397 de499029 Michael Hanselmann
  """Generic signal handler class.
2398 de499029 Michael Hanselmann

2399 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2400 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2401 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2402 58885d79 Iustin Pop
  signal was sent.
2403 58885d79 Iustin Pop

2404 58885d79 Iustin Pop
  @type signum: list
2405 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2406 58885d79 Iustin Pop
  @type called: boolean
2407 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2408 de499029 Michael Hanselmann

2409 de499029 Michael Hanselmann
  """
2410 de499029 Michael Hanselmann
  def __init__(self, signum):
2411 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2412 de499029 Michael Hanselmann

2413 58885d79 Iustin Pop
    @type signum: int or list of ints
2414 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2415 de499029 Michael Hanselmann

2416 de499029 Michael Hanselmann
    """
2417 6c52849e Guido Trotter
    self.signum = set(signum)
2418 de499029 Michael Hanselmann
    self.called = False
2419 de499029 Michael Hanselmann
2420 de499029 Michael Hanselmann
    self._previous = {}
2421 de499029 Michael Hanselmann
    try:
2422 de499029 Michael Hanselmann
      for signum in self.signum:
2423 de499029 Michael Hanselmann
        # Setup handler
2424 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2425 de499029 Michael Hanselmann
        try:
2426 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2427 de499029 Michael Hanselmann
        except:
2428 de499029 Michael Hanselmann
          # Restore previous handler
2429 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2430 de499029 Michael Hanselmann
          raise
2431 de499029 Michael Hanselmann
    except:
2432 de499029 Michael Hanselmann
      # Reset all handlers
2433 de499029 Michael Hanselmann
      self.Reset()
2434 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2435 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2436 de499029 Michael Hanselmann
      raise
2437 de499029 Michael Hanselmann
2438 de499029 Michael Hanselmann
  def __del__(self):
2439 de499029 Michael Hanselmann
    self.Reset()
2440 de499029 Michael Hanselmann
2441 de499029 Michael Hanselmann
  def Reset(self):
2442 de499029 Michael Hanselmann
    """Restore previous handler.
2443 de499029 Michael Hanselmann

2444 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2445 58885d79 Iustin Pop

2446 de499029 Michael Hanselmann
    """
2447 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2448 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2449 de499029 Michael Hanselmann
      # If successful, remove from dict
2450 de499029 Michael Hanselmann
      del self._previous[signum]
2451 de499029 Michael Hanselmann
2452 de499029 Michael Hanselmann
  def Clear(self):
2453 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2454 de499029 Michael Hanselmann

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

2457 de499029 Michael Hanselmann
    """
2458 de499029 Michael Hanselmann
    self.called = False
2459 de499029 Michael Hanselmann
2460 2d54e29c Iustin Pop
  # we don't care about arguments, but we leave them named for the future
2461 2d54e29c Iustin Pop
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2462 de499029 Michael Hanselmann
    """Actual signal handling function.
2463 de499029 Michael Hanselmann

2464 de499029 Michael Hanselmann
    """
2465 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2466 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2467 de499029 Michael Hanselmann
    self.called = True
2468 a2d2e1a7 Iustin Pop
2469 a2d2e1a7 Iustin Pop
2470 a2d2e1a7 Iustin Pop
class FieldSet(object):
2471 a2d2e1a7 Iustin Pop
  """A simple field set.
2472 a2d2e1a7 Iustin Pop

2473 a2d2e1a7 Iustin Pop
  Among the features are:
2474 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2475 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2476 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2477 a2d2e1a7 Iustin Pop

2478 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2479 a2d2e1a7 Iustin Pop

2480 a2d2e1a7 Iustin Pop
  """
2481 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2482 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2483 a2d2e1a7 Iustin Pop
2484 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2485 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2486 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2487 a2d2e1a7 Iustin Pop
2488 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2489 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2490 a2d2e1a7 Iustin Pop

2491 a2d2e1a7 Iustin Pop
    @type field: str
2492 a2d2e1a7 Iustin Pop
    @param field: the string to match
2493 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
2494 a2d2e1a7 Iustin Pop

2495 a2d2e1a7 Iustin Pop
    """
2496 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2497 a2d2e1a7 Iustin Pop
      return m
2498 6c881c52 Iustin Pop
    return None
2499 a2d2e1a7 Iustin Pop
2500 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2501 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2502 a2d2e1a7 Iustin Pop

2503 a2d2e1a7 Iustin Pop
    @type items: list
2504 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2505 a2d2e1a7 Iustin Pop
    @rtype: list
2506 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2507 a2d2e1a7 Iustin Pop

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