Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ e5a45a16

History | View | Annotate | Download (55.8 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 7c0d6283 Michael Hanselmann
67 a8083063 Iustin Pop
class RunResult(object):
68 58885d79 Iustin Pop
  """Holds the result of running external programs.
69 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

328 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
329 a8083063 Iustin Pop
  instead.
330 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

473 fee80e90 Guido Trotter
  """
474 fee80e90 Guido Trotter
  try:
475 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
476 d9f311d7 Iustin Pop
  except EnvironmentError, err:
477 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
478 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
479 d9f311d7 Iustin Pop
    return 0
480 fee80e90 Guido Trotter
481 fee80e90 Guido Trotter
  try:
482 fee80e90 Guido Trotter
    pid = int(pf.read())
483 d9f311d7 Iustin Pop
  except ValueError, err:
484 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
485 d9f311d7 Iustin Pop
    return 0
486 fee80e90 Guido Trotter
487 d9f311d7 Iustin Pop
  return pid
488 fee80e90 Guido Trotter
489 fee80e90 Guido Trotter
490 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
491 a8083063 Iustin Pop
  """Try to match a name against a list.
492 a8083063 Iustin Pop

493 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
494 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
495 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
496 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
497 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
498 58885d79 Iustin Pop
  'test1.example.org']}).
499 a8083063 Iustin Pop

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

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

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

520 bcf043c9 Iustin Pop
  """
521 89e1fc26 Iustin Pop
  def __init__(self, name=None):
522 bcf043c9 Iustin Pop
    """Initialize the host name object.
523 bcf043c9 Iustin Pop

524 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
525 89e1fc26 Iustin Pop
    name.
526 bcf043c9 Iustin Pop

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

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

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

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

554 58885d79 Iustin Pop
    @type hostname: str
555 58885d79 Iustin Pop
    @param hostname: hostname to look up
556 89e1fc26 Iustin Pop

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

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

575 58885d79 Iustin Pop
  @rtype: dict
576 58885d79 Iustin Pop
  @return:
577 58885d79 Iustin Pop
       Dictionary with keys volume name and values
578 58885d79 Iustin Pop
       the size of the volume
579 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

696 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
697 a8083063 Iustin Pop
  side.
698 a8083063 Iustin Pop

699 58885d79 Iustin Pop
  @type word: str
700 58885d79 Iustin Pop
  @param word: the word to check
701 58885d79 Iustin Pop
  @rtype: boolean
702 58885d79 Iustin Pop
  @return: True if the word is 'safe'
703 58885d79 Iustin Pop

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

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

716 58885d79 Iustin Pop
  @type template: str
717 58885d79 Iustin Pop
  @param template: the string holding the template for the
718 58885d79 Iustin Pop
      string formatting
719 58885d79 Iustin Pop
  @rtype: str
720 58885d79 Iustin Pop
  @return: the expanded command line
721 58885d79 Iustin Pop

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

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

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

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

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

814 58885d79 Iustin Pop
  @type file_name: str
815 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
816 58885d79 Iustin Pop
  @type key: str
817 58885d79 Iustin Pop
  @param key: string containing key
818 58885d79 Iustin Pop

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

843 58885d79 Iustin Pop
  @type file_name: str
844 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
845 58885d79 Iustin Pop
  @type key: str
846 58885d79 Iustin Pop
  @param key: string containing key
847 58885d79 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

1024 58885d79 Iustin Pop
  @type value: str
1025 58885d79 Iustin Pop
  @param value: the argument to be quoted
1026 58885d79 Iustin Pop
  @rtype: str
1027 58885d79 Iustin Pop
  @return: the quoted value
1028 58885d79 Iustin Pop

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

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

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

1051 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
1052 58885d79 Iustin Pop
  to it.
1053 58885d79 Iustin Pop

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

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

1097 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1098 caad16e2 Iustin Pop
  address.
1099 caad16e2 Iustin Pop

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

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

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

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

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

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

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

1152 59072e7e Michael Hanselmann
  """
1153 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
1154 59072e7e Michael Hanselmann
  try:
1155 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
1156 59072e7e Michael Hanselmann
  finally:
1157 59072e7e Michael Hanselmann
    f.close()
1158 087b34fe Iustin Pop
1159 087b34fe Iustin Pop
1160 33081d90 Iustin Pop
def GenerateSecret():
1161 33081d90 Iustin Pop
  """Generates a random secret.
1162 33081d90 Iustin Pop

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

1166 58885d79 Iustin Pop
  @rtype: str
1167 58885d79 Iustin Pop
  @return: a sha1 hexdigest of a block of 64 random bytes
1168 58885d79 Iustin Pop

1169 33081d90 Iustin Pop
  """
1170 7ffe8fba Carlos Valiente
  return sha1(os.urandom(64)).hexdigest()
1171 33081d90 Iustin Pop
1172 33081d90 Iustin Pop
1173 9dae41ad Guido Trotter
def EnsureDirs(dirs):
1174 9dae41ad Guido Trotter
  """Make required directories, if they don't exist.
1175 9dae41ad Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1689 58885d79 Iustin Pop
  @rtype: int
1690 58885d79 Iustin Pop

1691 4a8b186a Michael Hanselmann
  """
1692 4a8b186a Michael Hanselmann
  try:
1693 4a8b186a Michael Hanselmann
    port = socket.getservbyname("ganeti-noded", "tcp")
1694 4a8b186a Michael Hanselmann
  except socket.error:
1695 4a8b186a Michael Hanselmann
    port = constants.DEFAULT_NODED_PORT
1696 4a8b186a Michael Hanselmann
1697 4a8b186a Michael Hanselmann
  return port
1698 4a8b186a Michael Hanselmann
1699 4a8b186a Michael Hanselmann
1700 d21d09d6 Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program="",
1701 d21d09d6 Iustin Pop
                 multithreaded=False):
1702 82d9caef Iustin Pop
  """Configures the logging module.
1703 82d9caef Iustin Pop

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

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

1766 da961187 Guido Trotter
  This avoids things like /dir/../../other/path to be valid.
1767 da961187 Guido Trotter

1768 da961187 Guido Trotter
  """
1769 da961187 Guido Trotter
  return os.path.normpath(path) == path and os.path.isabs(path)
1770 82d9caef Iustin Pop
1771 f65f63ef Iustin Pop
def TailFile(fname, lines=20):
1772 f65f63ef Iustin Pop
  """Return the last lines from a file.
1773 f65f63ef Iustin Pop

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

1778 f65f63ef Iustin Pop
  @param fname: the file name
1779 f65f63ef Iustin Pop
  @type lines: int
1780 f65f63ef Iustin Pop
  @param lines: the (maximum) number of lines to return
1781 f65f63ef Iustin Pop

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

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

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

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

1837 835528af Iustin Pop
  @param names: set, list or tuple
1838 835528af Iustin Pop
  @return: a string with the formatted results
1839 835528af Iustin Pop

1840 835528af Iustin Pop
  """
1841 835528af Iustin Pop
  return ", ".join(["'%s'" % val for val in names])
1842 835528af Iustin Pop
1843 835528af Iustin Pop
1844 7996a135 Iustin Pop
def LockedMethod(fn):
1845 7996a135 Iustin Pop
  """Synchronized object access decorator.
1846 7996a135 Iustin Pop

1847 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1848 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1849 7996a135 Iustin Pop

1850 7996a135 Iustin Pop
  """
1851 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1852 e67bd559 Michael Hanselmann
    if debug_locks:
1853 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1854 e67bd559 Michael Hanselmann
1855 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1856 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1857 7996a135 Iustin Pop
    lock = self._lock
1858 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1859 7996a135 Iustin Pop
    lock.acquire()
1860 7996a135 Iustin Pop
    try:
1861 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1862 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1863 7996a135 Iustin Pop
    finally:
1864 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1865 7996a135 Iustin Pop
      lock.release()
1866 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1867 7996a135 Iustin Pop
    return result
1868 7996a135 Iustin Pop
  return wrapper
1869 eb0f0ce0 Michael Hanselmann
1870 eb0f0ce0 Michael Hanselmann
1871 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1872 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1873 eb0f0ce0 Michael Hanselmann

1874 58885d79 Iustin Pop
  @type fd: int
1875 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1876 58885d79 Iustin Pop

1877 eb0f0ce0 Michael Hanselmann
  """
1878 eb0f0ce0 Michael Hanselmann
  try:
1879 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1880 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1881 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1882 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1883 eb0f0ce0 Michael Hanselmann
    raise
1884 de499029 Michael Hanselmann
1885 de499029 Michael Hanselmann
1886 a87b4824 Michael Hanselmann
class FileLock(object):
1887 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1888 a87b4824 Michael Hanselmann

1889 a87b4824 Michael Hanselmann
  """
1890 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1891 58885d79 Iustin Pop
    """Constructor for FileLock.
1892 58885d79 Iustin Pop

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

1895 58885d79 Iustin Pop
    @type filename: str
1896 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1897 58885d79 Iustin Pop

1898 58885d79 Iustin Pop
    """
1899 a87b4824 Michael Hanselmann
    self.filename = filename
1900 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1901 a87b4824 Michael Hanselmann
1902 a87b4824 Michael Hanselmann
  def __del__(self):
1903 a87b4824 Michael Hanselmann
    self.Close()
1904 a87b4824 Michael Hanselmann
1905 a87b4824 Michael Hanselmann
  def Close(self):
1906 58885d79 Iustin Pop
    """Close the file and release the lock.
1907 58885d79 Iustin Pop

1908 58885d79 Iustin Pop
    """
1909 a87b4824 Michael Hanselmann
    if self.fd:
1910 a87b4824 Michael Hanselmann
      self.fd.close()
1911 a87b4824 Michael Hanselmann
      self.fd = None
1912 a87b4824 Michael Hanselmann
1913 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
1914 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
1915 aa74b828 Michael Hanselmann

1916 aa74b828 Michael Hanselmann
    @type flag: int
1917 58885d79 Iustin Pop
    @param flag: operation flag
1918 aa74b828 Michael Hanselmann
    @type blocking: bool
1919 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
1920 aa74b828 Michael Hanselmann
    @type timeout: None or float
1921 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
1922 aa74b828 Michael Hanselmann
                    non-blocking mode).
1923 aa74b828 Michael Hanselmann
    @type errmsg: string
1924 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
1925 aa74b828 Michael Hanselmann

1926 aa74b828 Michael Hanselmann
    """
1927 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1928 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
1929 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
1930 a87b4824 Michael Hanselmann
1931 aa74b828 Michael Hanselmann
    if timeout is not None:
1932 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1933 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
1934 a87b4824 Michael Hanselmann
1935 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
1936 aa74b828 Michael Hanselmann
    elif not blocking:
1937 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1938 aa74b828 Michael Hanselmann
      timeout_end = None
1939 aa74b828 Michael Hanselmann
1940 aa74b828 Michael Hanselmann
    retry = True
1941 aa74b828 Michael Hanselmann
    while retry:
1942 aa74b828 Michael Hanselmann
      try:
1943 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
1944 aa74b828 Michael Hanselmann
        retry = False
1945 aa74b828 Michael Hanselmann
      except IOError, err:
1946 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
1947 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
1948 aa74b828 Michael Hanselmann
            # Wait before trying again
1949 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
1950 aa74b828 Michael Hanselmann
          else:
1951 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
1952 aa74b828 Michael Hanselmann
        else:
1953 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
1954 aa74b828 Michael Hanselmann
          raise
1955 aa74b828 Michael Hanselmann
1956 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
1957 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1958 a87b4824 Michael Hanselmann

1959 58885d79 Iustin Pop
    @type blocking: boolean
1960 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1961 58885d79 Iustin Pop
        can lock the file or return immediately
1962 58885d79 Iustin Pop
    @type timeout: int or None
1963 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1964 58885d79 Iustin Pop
        (in blocking mode)
1965 58885d79 Iustin Pop

1966 a87b4824 Michael Hanselmann
    """
1967 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
1968 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1969 a87b4824 Michael Hanselmann
1970 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
1971 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1972 a87b4824 Michael Hanselmann

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

1980 a87b4824 Michael Hanselmann
    """
1981 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
1982 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1983 a87b4824 Michael Hanselmann
1984 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
1985 a87b4824 Michael Hanselmann
    """Unlocks the file.
1986 a87b4824 Michael Hanselmann

1987 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
1988 58885d79 Iustin Pop
    operation::
1989 58885d79 Iustin Pop

1990 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
1991 58885d79 Iustin Pop
      operations.
1992 58885d79 Iustin Pop

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

2000 a87b4824 Michael Hanselmann
    """
2001 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2002 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
2003 a87b4824 Michael Hanselmann
2004 a87b4824 Michael Hanselmann
2005 de499029 Michael Hanselmann
class SignalHandler(object):
2006 de499029 Michael Hanselmann
  """Generic signal handler class.
2007 de499029 Michael Hanselmann

2008 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
2009 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
2010 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
2011 58885d79 Iustin Pop
  signal was sent.
2012 58885d79 Iustin Pop

2013 58885d79 Iustin Pop
  @type signum: list
2014 58885d79 Iustin Pop
  @ivar signum: the signals we handle
2015 58885d79 Iustin Pop
  @type called: boolean
2016 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
2017 de499029 Michael Hanselmann

2018 de499029 Michael Hanselmann
  """
2019 de499029 Michael Hanselmann
  def __init__(self, signum):
2020 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
2021 de499029 Michael Hanselmann

2022 58885d79 Iustin Pop
    @type signum: int or list of ints
2023 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
2024 de499029 Michael Hanselmann

2025 de499029 Michael Hanselmann
    """
2026 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
2027 de499029 Michael Hanselmann
      self.signum = set([signum])
2028 de499029 Michael Hanselmann
    else:
2029 de499029 Michael Hanselmann
      self.signum = set(signum)
2030 de499029 Michael Hanselmann
2031 de499029 Michael Hanselmann
    self.called = False
2032 de499029 Michael Hanselmann
2033 de499029 Michael Hanselmann
    self._previous = {}
2034 de499029 Michael Hanselmann
    try:
2035 de499029 Michael Hanselmann
      for signum in self.signum:
2036 de499029 Michael Hanselmann
        # Setup handler
2037 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
2038 de499029 Michael Hanselmann
        try:
2039 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
2040 de499029 Michael Hanselmann
        except:
2041 de499029 Michael Hanselmann
          # Restore previous handler
2042 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
2043 de499029 Michael Hanselmann
          raise
2044 de499029 Michael Hanselmann
    except:
2045 de499029 Michael Hanselmann
      # Reset all handlers
2046 de499029 Michael Hanselmann
      self.Reset()
2047 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
2048 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
2049 de499029 Michael Hanselmann
      raise
2050 de499029 Michael Hanselmann
2051 de499029 Michael Hanselmann
  def __del__(self):
2052 de499029 Michael Hanselmann
    self.Reset()
2053 de499029 Michael Hanselmann
2054 de499029 Michael Hanselmann
  def Reset(self):
2055 de499029 Michael Hanselmann
    """Restore previous handler.
2056 de499029 Michael Hanselmann

2057 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
2058 58885d79 Iustin Pop

2059 de499029 Michael Hanselmann
    """
2060 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
2061 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
2062 de499029 Michael Hanselmann
      # If successful, remove from dict
2063 de499029 Michael Hanselmann
      del self._previous[signum]
2064 de499029 Michael Hanselmann
2065 de499029 Michael Hanselmann
  def Clear(self):
2066 58885d79 Iustin Pop
    """Unsets the L{called} flag.
2067 de499029 Michael Hanselmann

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

2070 de499029 Michael Hanselmann
    """
2071 de499029 Michael Hanselmann
    self.called = False
2072 de499029 Michael Hanselmann
2073 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
2074 de499029 Michael Hanselmann
    """Actual signal handling function.
2075 de499029 Michael Hanselmann

2076 de499029 Michael Hanselmann
    """
2077 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
2078 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
2079 de499029 Michael Hanselmann
    self.called = True
2080 a2d2e1a7 Iustin Pop
2081 a2d2e1a7 Iustin Pop
2082 a2d2e1a7 Iustin Pop
class FieldSet(object):
2083 a2d2e1a7 Iustin Pop
  """A simple field set.
2084 a2d2e1a7 Iustin Pop

2085 a2d2e1a7 Iustin Pop
  Among the features are:
2086 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
2087 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
2088 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
2089 a2d2e1a7 Iustin Pop

2090 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
2091 a2d2e1a7 Iustin Pop

2092 a2d2e1a7 Iustin Pop
  """
2093 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
2094 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
2095 a2d2e1a7 Iustin Pop
2096 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
2097 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
2098 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
2099 a2d2e1a7 Iustin Pop
2100 a2d2e1a7 Iustin Pop
  def Matches(self, field):
2101 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
2102 a2d2e1a7 Iustin Pop

2103 a2d2e1a7 Iustin Pop
    @type field: str
2104 a2d2e1a7 Iustin Pop
    @param field: the string to match
2105 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
2106 a2d2e1a7 Iustin Pop

2107 a2d2e1a7 Iustin Pop
    """
2108 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2109 a2d2e1a7 Iustin Pop
      return m
2110 a2d2e1a7 Iustin Pop
    return False
2111 a2d2e1a7 Iustin Pop
2112 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
2113 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
2114 a2d2e1a7 Iustin Pop

2115 a2d2e1a7 Iustin Pop
    @type items: list
2116 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
2117 a2d2e1a7 Iustin Pop
    @rtype: list
2118 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
2119 a2d2e1a7 Iustin Pop

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