Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 59303563

History | View | Annotate | Download (49.1 kB)

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

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

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

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

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

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

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

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

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

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

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

230 36117c2b Iustin Pop
  @type  cmd: string or list
231 36117c2b Iustin Pop
  @param cmd: Command to run
232 36117c2b Iustin Pop
  @type env: dict
233 36117c2b Iustin Pop
  @param env: The environment to use
234 36117c2b Iustin Pop
  @type via_shell: bool
235 36117c2b Iustin Pop
  @param via_shell: if we should run via the shell
236 36117c2b Iustin Pop
  @type output: str
237 36117c2b Iustin Pop
  @param output: the filename in which to save the output
238 8797df43 Iustin Pop
  @type cwd: string
239 8797df43 Iustin Pop
  @param cwd: the working directory for the program
240 36117c2b Iustin Pop
  @rtype: int
241 36117c2b Iustin Pop
  @return: the exit status
242 36117c2b Iustin Pop

243 36117c2b Iustin Pop
  """
244 36117c2b Iustin Pop
  fh = open(output, "a")
245 36117c2b Iustin Pop
  try:
246 36117c2b Iustin Pop
    child = subprocess.Popen(cmd, shell=via_shell,
247 36117c2b Iustin Pop
                             stderr=subprocess.STDOUT,
248 36117c2b Iustin Pop
                             stdout=fh,
249 36117c2b Iustin Pop
                             stdin=subprocess.PIPE,
250 8797df43 Iustin Pop
                             close_fds=True, env=env,
251 8797df43 Iustin Pop
                             cwd=cwd)
252 36117c2b Iustin Pop
253 36117c2b Iustin Pop
    child.stdin.close()
254 36117c2b Iustin Pop
    status = child.wait()
255 36117c2b Iustin Pop
  finally:
256 36117c2b Iustin Pop
    fh.close()
257 36117c2b Iustin Pop
  return status
258 a8083063 Iustin Pop
259 a8083063 Iustin Pop
260 a8083063 Iustin Pop
def RemoveFile(filename):
261 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
262 a8083063 Iustin Pop

263 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
264 a8083063 Iustin Pop
  errors are passed.
265 a8083063 Iustin Pop

266 58885d79 Iustin Pop
  @type filename: str
267 58885d79 Iustin Pop
  @param filename: the file to be removed
268 58885d79 Iustin Pop

269 a8083063 Iustin Pop
  """
270 a8083063 Iustin Pop
  try:
271 a8083063 Iustin Pop
    os.unlink(filename)
272 a8083063 Iustin Pop
  except OSError, err:
273 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
274 a8083063 Iustin Pop
      raise
275 a8083063 Iustin Pop
276 a8083063 Iustin Pop
277 a8083063 Iustin Pop
def _FingerprintFile(filename):
278 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
281 a8083063 Iustin Pop
  instead.
282 a8083063 Iustin Pop

283 58885d79 Iustin Pop
  @type filename: str
284 58885d79 Iustin Pop
  @param filename: the filename to checksum
285 58885d79 Iustin Pop
  @rtype: str
286 58885d79 Iustin Pop
  @return: the hex digest of the sha checksum of the contents
287 58885d79 Iustin Pop
      of the file
288 a8083063 Iustin Pop

289 a8083063 Iustin Pop
  """
290 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
291 a8083063 Iustin Pop
    return None
292 a8083063 Iustin Pop
293 a8083063 Iustin Pop
  f = open(filename)
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
  fp = sha.sha()
296 a8083063 Iustin Pop
  while True:
297 a8083063 Iustin Pop
    data = f.read(4096)
298 a8083063 Iustin Pop
    if not data:
299 a8083063 Iustin Pop
      break
300 a8083063 Iustin Pop
301 a8083063 Iustin Pop
    fp.update(data)
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
  return fp.hexdigest()
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
306 a8083063 Iustin Pop
def FingerprintFiles(files):
307 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
308 a8083063 Iustin Pop

309 58885d79 Iustin Pop
  @type files: list
310 58885d79 Iustin Pop
  @param files: the list of filename to fingerprint
311 58885d79 Iustin Pop
  @rtype: dict
312 58885d79 Iustin Pop
  @return: a dictionary filename: fingerprint, holding only
313 58885d79 Iustin Pop
      existing files
314 a8083063 Iustin Pop

315 a8083063 Iustin Pop
  """
316 a8083063 Iustin Pop
  ret = {}
317 a8083063 Iustin Pop
318 a8083063 Iustin Pop
  for filename in files:
319 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
320 a8083063 Iustin Pop
    if cksum:
321 a8083063 Iustin Pop
      ret[filename] = cksum
322 a8083063 Iustin Pop
323 a8083063 Iustin Pop
  return ret
324 a8083063 Iustin Pop
325 a8083063 Iustin Pop
326 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
327 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
328 a8083063 Iustin Pop

329 58885d79 Iustin Pop
  For the given dictionaries I{target} and I{template}, ensure
330 58885d79 Iustin Pop
  I{target} has all the keys from I{template}. Missing keys are added
331 58885d79 Iustin Pop
  with values from template.
332 a8083063 Iustin Pop

333 58885d79 Iustin Pop
  @type target: dict
334 58885d79 Iustin Pop
  @param target: the dictionary to update
335 58885d79 Iustin Pop
  @type template: dict
336 58885d79 Iustin Pop
  @param template: the dictionary holding the default values
337 58885d79 Iustin Pop
  @type logname: str or None
338 58885d79 Iustin Pop
  @param logname: if not None, causes the missing keys to be
339 58885d79 Iustin Pop
      logged with this name
340 a8083063 Iustin Pop

341 a8083063 Iustin Pop
  """
342 a8083063 Iustin Pop
  missing = []
343 a8083063 Iustin Pop
  for k in template:
344 a8083063 Iustin Pop
    if k not in target:
345 a8083063 Iustin Pop
      missing.append(k)
346 a8083063 Iustin Pop
      target[k] = template[k]
347 a8083063 Iustin Pop
348 a8083063 Iustin Pop
  if missing and logname:
349 bb698c1f Iustin Pop
    logging.warning('%s missing keys %s', logname, ', '.join(missing))
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
def IsProcessAlive(pid):
353 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
354 a8083063 Iustin Pop

355 44bf25ff Iustin Pop
  @note: zombie status is not handled, so zombie processes
356 44bf25ff Iustin Pop
      will be returned as alive
357 58885d79 Iustin Pop
  @type pid: int
358 58885d79 Iustin Pop
  @param pid: the process ID to check
359 58885d79 Iustin Pop
  @rtype: boolean
360 58885d79 Iustin Pop
  @return: True if the process exists
361 a8083063 Iustin Pop

362 a8083063 Iustin Pop
  """
363 d9f311d7 Iustin Pop
  if pid <= 0:
364 d9f311d7 Iustin Pop
    return False
365 d9f311d7 Iustin Pop
366 a8083063 Iustin Pop
  try:
367 44bf25ff Iustin Pop
    os.stat("/proc/%d/status" % pid)
368 44bf25ff Iustin Pop
    return True
369 44bf25ff Iustin Pop
  except EnvironmentError, err:
370 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
371 a8083063 Iustin Pop
      return False
372 44bf25ff Iustin Pop
    raise
373 a8083063 Iustin Pop
374 a8083063 Iustin Pop
375 d9f311d7 Iustin Pop
def ReadPidFile(pidfile):
376 58885d79 Iustin Pop
  """Read a pid from a file.
377 fee80e90 Guido Trotter

378 58885d79 Iustin Pop
  @type  pidfile: string
379 58885d79 Iustin Pop
  @param pidfile: path to the file containing the pid
380 58885d79 Iustin Pop
  @rtype: int
381 d9f311d7 Iustin Pop
  @return: The process id, if the file exista and contains a valid PID,
382 d9f311d7 Iustin Pop
           otherwise 0
383 fee80e90 Guido Trotter

384 fee80e90 Guido Trotter
  """
385 fee80e90 Guido Trotter
  try:
386 fee80e90 Guido Trotter
    pf = open(pidfile, 'r')
387 d9f311d7 Iustin Pop
  except EnvironmentError, err:
388 d9f311d7 Iustin Pop
    if err.errno != errno.ENOENT:
389 d9f311d7 Iustin Pop
      logging.exception("Can't read pid file?!")
390 d9f311d7 Iustin Pop
    return 0
391 fee80e90 Guido Trotter
392 fee80e90 Guido Trotter
  try:
393 fee80e90 Guido Trotter
    pid = int(pf.read())
394 d9f311d7 Iustin Pop
  except ValueError, err:
395 8161a646 Iustin Pop
    logging.info("Can't parse pid file contents", exc_info=True)
396 d9f311d7 Iustin Pop
    return 0
397 fee80e90 Guido Trotter
398 d9f311d7 Iustin Pop
  return pid
399 fee80e90 Guido Trotter
400 fee80e90 Guido Trotter
401 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
402 a8083063 Iustin Pop
  """Try to match a name against a list.
403 a8083063 Iustin Pop

404 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
405 58885d79 Iustin Pop
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
406 58885d79 Iustin Pop
  this list, I{'test1'} as well as I{'test1.example'} will match, but
407 58885d79 Iustin Pop
  not I{'test1.ex'}. A multiple match will be considered as no match
408 58885d79 Iustin Pop
  at all (e.g. I{'test1'} against C{['test1.example.com',
409 58885d79 Iustin Pop
  'test1.example.org']}).
410 a8083063 Iustin Pop

411 58885d79 Iustin Pop
  @type key: str
412 58885d79 Iustin Pop
  @param key: the name to be searched
413 58885d79 Iustin Pop
  @type name_list: list
414 58885d79 Iustin Pop
  @param name_list: the list of strings against which to search the key
415 a8083063 Iustin Pop

416 58885d79 Iustin Pop
  @rtype: None or str
417 58885d79 Iustin Pop
  @return: None if there is no match I{or} if there are multiple matches,
418 58885d79 Iustin Pop
      otherwise the element from the list which matches
419 a8083063 Iustin Pop

420 a8083063 Iustin Pop
  """
421 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
422 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
423 a8083063 Iustin Pop
  if len(names_filtered) != 1:
424 a8083063 Iustin Pop
    return None
425 a8083063 Iustin Pop
  return names_filtered[0]
426 a8083063 Iustin Pop
427 a8083063 Iustin Pop
428 bcf043c9 Iustin Pop
class HostInfo:
429 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
430 bcf043c9 Iustin Pop

431 bcf043c9 Iustin Pop
  """
432 89e1fc26 Iustin Pop
  def __init__(self, name=None):
433 bcf043c9 Iustin Pop
    """Initialize the host name object.
434 bcf043c9 Iustin Pop

435 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
436 89e1fc26 Iustin Pop
    name.
437 bcf043c9 Iustin Pop

438 bcf043c9 Iustin Pop
    """
439 89e1fc26 Iustin Pop
    if name is None:
440 89e1fc26 Iustin Pop
      name = self.SysName()
441 89e1fc26 Iustin Pop
442 89e1fc26 Iustin Pop
    self.query = name
443 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
444 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
445 bcf043c9 Iustin Pop
446 c8a0948f Michael Hanselmann
  def ShortName(self):
447 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
448 c8a0948f Michael Hanselmann

449 c8a0948f Michael Hanselmann
    """
450 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
451 c8a0948f Michael Hanselmann
452 89e1fc26 Iustin Pop
  @staticmethod
453 89e1fc26 Iustin Pop
  def SysName():
454 89e1fc26 Iustin Pop
    """Return the current system's name.
455 bcf043c9 Iustin Pop

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

458 89e1fc26 Iustin Pop
    """
459 89e1fc26 Iustin Pop
    return socket.gethostname()
460 a8083063 Iustin Pop
461 89e1fc26 Iustin Pop
  @staticmethod
462 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
463 89e1fc26 Iustin Pop
    """Look up hostname
464 a8083063 Iustin Pop

465 58885d79 Iustin Pop
    @type hostname: str
466 58885d79 Iustin Pop
    @param hostname: hostname to look up
467 89e1fc26 Iustin Pop

468 58885d79 Iustin Pop
    @rtype: tuple
469 58885d79 Iustin Pop
    @return: a tuple (name, aliases, ipaddrs) as returned by
470 58885d79 Iustin Pop
        C{socket.gethostbyname_ex}
471 58885d79 Iustin Pop
    @raise errors.ResolverError: in case of errors in resolving
472 89e1fc26 Iustin Pop

473 89e1fc26 Iustin Pop
    """
474 89e1fc26 Iustin Pop
    try:
475 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
476 89e1fc26 Iustin Pop
    except socket.gaierror, err:
477 89e1fc26 Iustin Pop
      # hostname not found in DNS
478 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
479 a8083063 Iustin Pop
480 89e1fc26 Iustin Pop
    return result
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
483 a8083063 Iustin Pop
def ListVolumeGroups():
484 a8083063 Iustin Pop
  """List volume groups and their size
485 a8083063 Iustin Pop

486 58885d79 Iustin Pop
  @rtype: dict
487 58885d79 Iustin Pop
  @return:
488 58885d79 Iustin Pop
       Dictionary with keys volume name and values
489 58885d79 Iustin Pop
       the size of the volume
490 a8083063 Iustin Pop

491 a8083063 Iustin Pop
  """
492 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
493 a8083063 Iustin Pop
  result = RunCmd(command)
494 a8083063 Iustin Pop
  retval = {}
495 a8083063 Iustin Pop
  if result.failed:
496 a8083063 Iustin Pop
    return retval
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
499 a8083063 Iustin Pop
    try:
500 a8083063 Iustin Pop
      name, size = line.split()
501 a8083063 Iustin Pop
      size = int(float(size))
502 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
503 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
504 a8083063 Iustin Pop
      continue
505 a8083063 Iustin Pop
506 a8083063 Iustin Pop
    retval[name] = size
507 a8083063 Iustin Pop
508 a8083063 Iustin Pop
  return retval
509 a8083063 Iustin Pop
510 a8083063 Iustin Pop
511 a8083063 Iustin Pop
def BridgeExists(bridge):
512 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
513 a8083063 Iustin Pop

514 58885d79 Iustin Pop
  @type bridge: str
515 58885d79 Iustin Pop
  @param bridge: the bridge name to check
516 58885d79 Iustin Pop
  @rtype: boolean
517 58885d79 Iustin Pop
  @return: True if it does
518 a8083063 Iustin Pop

519 a8083063 Iustin Pop
  """
520 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
521 a8083063 Iustin Pop
522 a8083063 Iustin Pop
523 a8083063 Iustin Pop
def NiceSort(name_list):
524 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
525 a8083063 Iustin Pop

526 58885d79 Iustin Pop
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
527 58885d79 Iustin Pop
  will sort the list in the logical order C{['a1', 'a2', 'a10',
528 58885d79 Iustin Pop
  'a11']}.
529 a8083063 Iustin Pop

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

534 58885d79 Iustin Pop
  @type name_list: list
535 58885d79 Iustin Pop
  @param name_list: the names to be sorted
536 58885d79 Iustin Pop
  @rtype: list
537 58885d79 Iustin Pop
  @return: a copy of the name list sorted with our algorithm
538 a8083063 Iustin Pop

539 a8083063 Iustin Pop
  """
540 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
541 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
542 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
543 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
544 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
545 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
546 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
547 a8083063 Iustin Pop
  def _TryInt(val):
548 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
549 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
550 a8083063 Iustin Pop
      return val
551 a8083063 Iustin Pop
    rval = int(val)
552 a8083063 Iustin Pop
    return rval
553 a8083063 Iustin Pop
554 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
555 a8083063 Iustin Pop
             for name in name_list]
556 a8083063 Iustin Pop
  to_sort.sort()
557 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
558 a8083063 Iustin Pop
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
def TryConvert(fn, val):
561 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
562 a8083063 Iustin Pop

563 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
564 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
565 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
566 58885d79 Iustin Pop
  exceptions are propagated to the caller.
567 58885d79 Iustin Pop

568 58885d79 Iustin Pop
  @type fn: callable
569 58885d79 Iustin Pop
  @param fn: function to apply to the value
570 58885d79 Iustin Pop
  @param val: the value to be converted
571 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
572 58885d79 Iustin Pop
      otherwise the original value.
573 a8083063 Iustin Pop

574 a8083063 Iustin Pop
  """
575 a8083063 Iustin Pop
  try:
576 a8083063 Iustin Pop
    nv = fn(val)
577 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
578 a8083063 Iustin Pop
    nv = val
579 a8083063 Iustin Pop
  return nv
580 a8083063 Iustin Pop
581 a8083063 Iustin Pop
582 a8083063 Iustin Pop
def IsValidIP(ip):
583 58885d79 Iustin Pop
  """Verifies the syntax of an IPv4 address.
584 a8083063 Iustin Pop

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

588 58885d79 Iustin Pop
  @type ip: str
589 58885d79 Iustin Pop
  @param ip: the address to be checked
590 58885d79 Iustin Pop
  @rtype: a regular expression match object
591 58885d79 Iustin Pop
  @return: a regular epression match object, or None if the
592 58885d79 Iustin Pop
      address is not valid
593 a8083063 Iustin Pop

594 a8083063 Iustin Pop
  """
595 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
596 58885d79 Iustin Pop
  #TODO: convert and return only boolean
597 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
598 a8083063 Iustin Pop
599 a8083063 Iustin Pop
600 a8083063 Iustin Pop
def IsValidShellParam(word):
601 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
602 a8083063 Iustin Pop

603 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
604 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
605 a8083063 Iustin Pop
  the actual command.
606 a8083063 Iustin Pop

607 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
608 a8083063 Iustin Pop
  side.
609 a8083063 Iustin Pop

610 58885d79 Iustin Pop
  @type word: str
611 58885d79 Iustin Pop
  @param word: the word to check
612 58885d79 Iustin Pop
  @rtype: boolean
613 58885d79 Iustin Pop
  @return: True if the word is 'safe'
614 58885d79 Iustin Pop

615 a8083063 Iustin Pop
  """
616 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
617 a8083063 Iustin Pop
618 a8083063 Iustin Pop
619 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
620 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
621 a8083063 Iustin Pop

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

627 58885d79 Iustin Pop
  @type template: str
628 58885d79 Iustin Pop
  @param template: the string holding the template for the
629 58885d79 Iustin Pop
      string formatting
630 58885d79 Iustin Pop
  @rtype: str
631 58885d79 Iustin Pop
  @return: the expanded command line
632 58885d79 Iustin Pop

633 a8083063 Iustin Pop
  """
634 a8083063 Iustin Pop
  for word in args:
635 a8083063 Iustin Pop
    if not IsValidShellParam(word):
636 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
637 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
638 a8083063 Iustin Pop
  return template % args
639 a8083063 Iustin Pop
640 a8083063 Iustin Pop
641 9fbfbb7b Iustin Pop
def FormatUnit(value, units):
642 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
643 a8083063 Iustin Pop

644 58885d79 Iustin Pop
  @type value: int
645 58885d79 Iustin Pop
  @param value: integer representing the value in MiB (1048576)
646 9fbfbb7b Iustin Pop
  @type units: char
647 9fbfbb7b Iustin Pop
  @param units: the type of formatting we should do:
648 9fbfbb7b Iustin Pop
      - 'h' for automatic scaling
649 9fbfbb7b Iustin Pop
      - 'm' for MiBs
650 9fbfbb7b Iustin Pop
      - 'g' for GiBs
651 9fbfbb7b Iustin Pop
      - 't' for TiBs
652 58885d79 Iustin Pop
  @rtype: str
653 58885d79 Iustin Pop
  @return: the formatted value (with suffix)
654 a8083063 Iustin Pop

655 a8083063 Iustin Pop
  """
656 9fbfbb7b Iustin Pop
  if units not in ('m', 'g', 't', 'h'):
657 9fbfbb7b Iustin Pop
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
658 a8083063 Iustin Pop
659 9fbfbb7b Iustin Pop
  suffix = ''
660 9fbfbb7b Iustin Pop
661 9fbfbb7b Iustin Pop
  if units == 'm' or (units == 'h' and value < 1024):
662 9fbfbb7b Iustin Pop
    if units == 'h':
663 9fbfbb7b Iustin Pop
      suffix = 'M'
664 9fbfbb7b Iustin Pop
    return "%d%s" % (round(value, 0), suffix)
665 9fbfbb7b Iustin Pop
666 9fbfbb7b Iustin Pop
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
667 9fbfbb7b Iustin Pop
    if units == 'h':
668 9fbfbb7b Iustin Pop
      suffix = 'G'
669 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
670 a8083063 Iustin Pop
671 a8083063 Iustin Pop
  else:
672 9fbfbb7b Iustin Pop
    if units == 'h':
673 9fbfbb7b Iustin Pop
      suffix = 'T'
674 9fbfbb7b Iustin Pop
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
675 a8083063 Iustin Pop
676 a8083063 Iustin Pop
677 a8083063 Iustin Pop
def ParseUnit(input_string):
678 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
679 a8083063 Iustin Pop

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

684 a8083063 Iustin Pop
  """
685 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
686 a8083063 Iustin Pop
  if not m:
687 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
688 a8083063 Iustin Pop
689 a8083063 Iustin Pop
  value = float(m.groups()[0])
690 a8083063 Iustin Pop
691 a8083063 Iustin Pop
  unit = m.groups()[1]
692 a8083063 Iustin Pop
  if unit:
693 a8083063 Iustin Pop
    lcunit = unit.lower()
694 a8083063 Iustin Pop
  else:
695 a8083063 Iustin Pop
    lcunit = 'm'
696 a8083063 Iustin Pop
697 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
698 a8083063 Iustin Pop
    # Value already in MiB
699 a8083063 Iustin Pop
    pass
700 a8083063 Iustin Pop
701 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
702 a8083063 Iustin Pop
    value *= 1024
703 a8083063 Iustin Pop
704 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
705 a8083063 Iustin Pop
    value *= 1024 * 1024
706 a8083063 Iustin Pop
707 a8083063 Iustin Pop
  else:
708 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
709 a8083063 Iustin Pop
710 a8083063 Iustin Pop
  # Make sure we round up
711 a8083063 Iustin Pop
  if int(value) < value:
712 a8083063 Iustin Pop
    value += 1
713 a8083063 Iustin Pop
714 a8083063 Iustin Pop
  # Round up to the next multiple of 4
715 a8083063 Iustin Pop
  value = int(value)
716 a8083063 Iustin Pop
  if value % 4:
717 a8083063 Iustin Pop
    value += 4 - value % 4
718 a8083063 Iustin Pop
719 a8083063 Iustin Pop
  return value
720 a8083063 Iustin Pop
721 a8083063 Iustin Pop
722 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
723 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
724 a8083063 Iustin Pop

725 58885d79 Iustin Pop
  @type file_name: str
726 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
727 58885d79 Iustin Pop
  @type key: str
728 58885d79 Iustin Pop
  @param key: string containing key
729 58885d79 Iustin Pop

730 a8083063 Iustin Pop
  """
731 a8083063 Iustin Pop
  key_fields = key.split()
732 a8083063 Iustin Pop
733 a8083063 Iustin Pop
  f = open(file_name, 'a+')
734 a8083063 Iustin Pop
  try:
735 a8083063 Iustin Pop
    nl = True
736 a8083063 Iustin Pop
    for line in f:
737 a8083063 Iustin Pop
      # Ignore whitespace changes
738 a8083063 Iustin Pop
      if line.split() == key_fields:
739 a8083063 Iustin Pop
        break
740 a8083063 Iustin Pop
      nl = line.endswith('\n')
741 a8083063 Iustin Pop
    else:
742 a8083063 Iustin Pop
      if not nl:
743 a8083063 Iustin Pop
        f.write("\n")
744 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
745 a8083063 Iustin Pop
      f.write("\n")
746 a8083063 Iustin Pop
      f.flush()
747 a8083063 Iustin Pop
  finally:
748 a8083063 Iustin Pop
    f.close()
749 a8083063 Iustin Pop
750 a8083063 Iustin Pop
751 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
752 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
753 a8083063 Iustin Pop

754 58885d79 Iustin Pop
  @type file_name: str
755 58885d79 Iustin Pop
  @param file_name: path to authorized_keys file
756 58885d79 Iustin Pop
  @type key: str
757 58885d79 Iustin Pop
  @param key: string containing key
758 58885d79 Iustin Pop

759 a8083063 Iustin Pop
  """
760 a8083063 Iustin Pop
  key_fields = key.split()
761 a8083063 Iustin Pop
762 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
763 a8083063 Iustin Pop
  try:
764 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
765 a8083063 Iustin Pop
    try:
766 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
767 59f82e3f Michael Hanselmann
      try:
768 59f82e3f Michael Hanselmann
        for line in f:
769 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
770 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
771 59f82e3f Michael Hanselmann
            out.write(line)
772 899d2a81 Michael Hanselmann
773 899d2a81 Michael Hanselmann
        out.flush()
774 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
775 899d2a81 Michael Hanselmann
      finally:
776 899d2a81 Michael Hanselmann
        f.close()
777 899d2a81 Michael Hanselmann
    finally:
778 899d2a81 Michael Hanselmann
      out.close()
779 899d2a81 Michael Hanselmann
  except:
780 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
781 899d2a81 Michael Hanselmann
    raise
782 899d2a81 Michael Hanselmann
783 899d2a81 Michael Hanselmann
784 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
785 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
786 899d2a81 Michael Hanselmann

787 58885d79 Iustin Pop
  @type file_name: str
788 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
789 58885d79 Iustin Pop
  @type ip: str
790 58885d79 Iustin Pop
  @param ip: the IP address
791 58885d79 Iustin Pop
  @type hostname: str
792 58885d79 Iustin Pop
  @param hostname: the hostname to be added
793 58885d79 Iustin Pop
  @type aliases: list
794 58885d79 Iustin Pop
  @param aliases: the list of aliases to add for the hostname
795 58885d79 Iustin Pop

796 899d2a81 Michael Hanselmann
  """
797 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
798 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
799 7fbb1f65 Michael Hanselmann
800 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
801 899d2a81 Michael Hanselmann
  try:
802 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
803 9440aeab Michael Hanselmann
    try:
804 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
805 9440aeab Michael Hanselmann
      try:
806 9440aeab Michael Hanselmann
        written = False
807 9440aeab Michael Hanselmann
        for line in f:
808 9440aeab Michael Hanselmann
          fields = line.split()
809 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
810 9440aeab Michael Hanselmann
            continue
811 9440aeab Michael Hanselmann
          out.write(line)
812 9440aeab Michael Hanselmann
813 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
814 9440aeab Michael Hanselmann
        if aliases:
815 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
816 9440aeab Michael Hanselmann
        out.write('\n')
817 9440aeab Michael Hanselmann
818 9440aeab Michael Hanselmann
        out.flush()
819 2e3e75b7 Michael Hanselmann
        os.fsync(out)
820 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
821 9440aeab Michael Hanselmann
      finally:
822 9440aeab Michael Hanselmann
        f.close()
823 9440aeab Michael Hanselmann
    finally:
824 9440aeab Michael Hanselmann
      out.close()
825 9440aeab Michael Hanselmann
  except:
826 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
827 9440aeab Michael Hanselmann
    raise
828 899d2a81 Michael Hanselmann
829 899d2a81 Michael Hanselmann
830 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
831 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
832 d9c02ca6 Michael Hanselmann

833 58885d79 Iustin Pop
  @type hostname: str
834 58885d79 Iustin Pop
  @param hostname: a hostname that will be resolved and added to
835 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
836 58885d79 Iustin Pop

837 d9c02ca6 Michael Hanselmann
  """
838 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
839 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
840 d9c02ca6 Michael Hanselmann
841 d9c02ca6 Michael Hanselmann
842 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
843 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
844 899d2a81 Michael Hanselmann

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

847 58885d79 Iustin Pop
  @type file_name: str
848 58885d79 Iustin Pop
  @param file_name: path to the file to modify (usually C{/etc/hosts})
849 58885d79 Iustin Pop
  @type hostname: str
850 58885d79 Iustin Pop
  @param hostname: the hostname to be removed
851 58885d79 Iustin Pop

852 899d2a81 Michael Hanselmann
  """
853 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
854 899d2a81 Michael Hanselmann
  try:
855 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
856 899d2a81 Michael Hanselmann
    try:
857 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
858 899d2a81 Michael Hanselmann
      try:
859 899d2a81 Michael Hanselmann
        for line in f:
860 899d2a81 Michael Hanselmann
          fields = line.split()
861 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
862 899d2a81 Michael Hanselmann
            names = fields[1:]
863 899d2a81 Michael Hanselmann
            if hostname in names:
864 899d2a81 Michael Hanselmann
              while hostname in names:
865 899d2a81 Michael Hanselmann
                names.remove(hostname)
866 899d2a81 Michael Hanselmann
              if names:
867 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
868 899d2a81 Michael Hanselmann
              continue
869 899d2a81 Michael Hanselmann
870 899d2a81 Michael Hanselmann
          out.write(line)
871 59f82e3f Michael Hanselmann
872 59f82e3f Michael Hanselmann
        out.flush()
873 2e3e75b7 Michael Hanselmann
        os.fsync(out)
874 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
875 59f82e3f Michael Hanselmann
      finally:
876 59f82e3f Michael Hanselmann
        f.close()
877 a8083063 Iustin Pop
    finally:
878 59f82e3f Michael Hanselmann
      out.close()
879 59f82e3f Michael Hanselmann
  except:
880 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
881 59f82e3f Michael Hanselmann
    raise
882 a8083063 Iustin Pop
883 a8083063 Iustin Pop
884 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
885 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
886 d9c02ca6 Michael Hanselmann

887 58885d79 Iustin Pop
  @type hostname: str
888 58885d79 Iustin Pop
  @param hostname: hostname that will be resolved and its
889 58885d79 Iustin Pop
      full and shot name will be removed from
890 58885d79 Iustin Pop
      L{constants.ETC_HOSTS}
891 58885d79 Iustin Pop

892 d9c02ca6 Michael Hanselmann
  """
893 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
894 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
895 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
896 d9c02ca6 Michael Hanselmann
897 d9c02ca6 Michael Hanselmann
898 a8083063 Iustin Pop
def CreateBackup(file_name):
899 a8083063 Iustin Pop
  """Creates a backup of a file.
900 a8083063 Iustin Pop

901 58885d79 Iustin Pop
  @type file_name: str
902 58885d79 Iustin Pop
  @param file_name: file to be backed up
903 58885d79 Iustin Pop
  @rtype: str
904 58885d79 Iustin Pop
  @return: the path to the newly created backup
905 58885d79 Iustin Pop
  @raise errors.ProgrammerError: for invalid file names
906 a8083063 Iustin Pop

907 a8083063 Iustin Pop
  """
908 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
909 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
910 3ecf6786 Iustin Pop
                                file_name)
911 a8083063 Iustin Pop
912 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
913 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
914 081b1e69 Michael Hanselmann
915 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
916 081b1e69 Michael Hanselmann
  try:
917 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
918 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
919 081b1e69 Michael Hanselmann
    try:
920 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
921 081b1e69 Michael Hanselmann
    finally:
922 081b1e69 Michael Hanselmann
      fdst.close()
923 081b1e69 Michael Hanselmann
  finally:
924 081b1e69 Michael Hanselmann
    fsrc.close()
925 081b1e69 Michael Hanselmann
926 a8083063 Iustin Pop
  return backup_name
927 a8083063 Iustin Pop
928 a8083063 Iustin Pop
929 a8083063 Iustin Pop
def ShellQuote(value):
930 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
931 3ecf6786 Iustin Pop

932 58885d79 Iustin Pop
  @type value: str
933 58885d79 Iustin Pop
  @param value: the argument to be quoted
934 58885d79 Iustin Pop
  @rtype: str
935 58885d79 Iustin Pop
  @return: the quoted value
936 58885d79 Iustin Pop

937 a8083063 Iustin Pop
  """
938 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
939 a8083063 Iustin Pop
    return value
940 a8083063 Iustin Pop
  else:
941 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
942 a8083063 Iustin Pop
943 a8083063 Iustin Pop
944 a8083063 Iustin Pop
def ShellQuoteArgs(args):
945 58885d79 Iustin Pop
  """Quotes a list of shell arguments.
946 58885d79 Iustin Pop

947 58885d79 Iustin Pop
  @type args: list
948 58885d79 Iustin Pop
  @param args: list of arguments to be quoted
949 58885d79 Iustin Pop
  @rtype: str
950 58885d79 Iustin Pop
  @return: the quoted arguments concatenaned with spaces
951 a8083063 Iustin Pop

952 a8083063 Iustin Pop
  """
953 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
954 88d14415 Michael Hanselmann
955 88d14415 Michael Hanselmann
956 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
957 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
958 2c30e9d7 Alexander Schreiber

959 58885d79 Iustin Pop
  Check if the given IP is reachable by doing attempting a TCP connect
960 58885d79 Iustin Pop
  to it.
961 58885d79 Iustin Pop

962 58885d79 Iustin Pop
  @type target: str
963 58885d79 Iustin Pop
  @param target: the IP or hostname to ping
964 58885d79 Iustin Pop
  @type port: int
965 58885d79 Iustin Pop
  @param port: the port to connect to
966 58885d79 Iustin Pop
  @type timeout: int
967 58885d79 Iustin Pop
  @param timeout: the timeout on the connection attemp
968 58885d79 Iustin Pop
  @type live_port_needed: boolean
969 58885d79 Iustin Pop
  @param live_port_needed: whether a closed port will cause the
970 58885d79 Iustin Pop
      function to return failure, as if there was a timeout
971 58885d79 Iustin Pop
  @type source: str or None
972 58885d79 Iustin Pop
  @param source: if specified, will cause the connect to be made
973 58885d79 Iustin Pop
      from this specific source address; failures to bind other
974 58885d79 Iustin Pop
      than C{EADDRNOTAVAIL} will be ignored
975 2c30e9d7 Alexander Schreiber

976 2c30e9d7 Alexander Schreiber
  """
977 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
978 2c30e9d7 Alexander Schreiber
979 2c30e9d7 Alexander Schreiber
  sucess = False
980 2c30e9d7 Alexander Schreiber
981 b15d625f Iustin Pop
  if source is not None:
982 b15d625f Iustin Pop
    try:
983 b15d625f Iustin Pop
      sock.bind((source, 0))
984 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
985 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
986 b15d625f Iustin Pop
        success = False
987 2c30e9d7 Alexander Schreiber
988 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
989 2c30e9d7 Alexander Schreiber
990 2c30e9d7 Alexander Schreiber
  try:
991 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
992 2c30e9d7 Alexander Schreiber
    sock.close()
993 2c30e9d7 Alexander Schreiber
    success = True
994 2c30e9d7 Alexander Schreiber
  except socket.timeout:
995 2c30e9d7 Alexander Schreiber
    success = False
996 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
997 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
998 2c30e9d7 Alexander Schreiber
999 2c30e9d7 Alexander Schreiber
  return success
1000 eedbda4b Michael Hanselmann
1001 eedbda4b Michael Hanselmann
1002 caad16e2 Iustin Pop
def OwnIpAddress(address):
1003 caad16e2 Iustin Pop
  """Check if the current host has the the given IP address.
1004 caad16e2 Iustin Pop

1005 58885d79 Iustin Pop
  Currently this is done by TCP-pinging the address from the loopback
1006 caad16e2 Iustin Pop
  address.
1007 caad16e2 Iustin Pop

1008 caad16e2 Iustin Pop
  @type address: string
1009 caad16e2 Iustin Pop
  @param address: the addres to check
1010 caad16e2 Iustin Pop
  @rtype: bool
1011 58885d79 Iustin Pop
  @return: True if we own the address
1012 caad16e2 Iustin Pop

1013 caad16e2 Iustin Pop
  """
1014 caad16e2 Iustin Pop
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1015 caad16e2 Iustin Pop
                 source=constants.LOCALHOST_IP_ADDRESS)
1016 caad16e2 Iustin Pop
1017 caad16e2 Iustin Pop
1018 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
1019 58885d79 Iustin Pop
  """Returns a list of visible files in a directory.
1020 58885d79 Iustin Pop

1021 58885d79 Iustin Pop
  @type path: str
1022 58885d79 Iustin Pop
  @param path: the directory to enumerate
1023 58885d79 Iustin Pop
  @rtype: list
1024 58885d79 Iustin Pop
  @return: the list of all files not starting with a dot
1025 eedbda4b Michael Hanselmann

1026 eedbda4b Michael Hanselmann
  """
1027 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1028 f3299a07 Michael Hanselmann
  files.sort()
1029 f3299a07 Michael Hanselmann
  return files
1030 2f8b60b3 Iustin Pop
1031 2f8b60b3 Iustin Pop
1032 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
1033 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
1034 257f4c0a Iustin Pop

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

1039 2f8b60b3 Iustin Pop
  """
1040 2f8b60b3 Iustin Pop
  try:
1041 257f4c0a Iustin Pop
    if isinstance(user, basestring):
1042 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
1043 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
1044 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
1045 257f4c0a Iustin Pop
    else:
1046 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1047 257f4c0a Iustin Pop
                                   type(user))
1048 2f8b60b3 Iustin Pop
  except KeyError:
1049 2f8b60b3 Iustin Pop
    return default
1050 2f8b60b3 Iustin Pop
  return result.pw_dir
1051 59072e7e Michael Hanselmann
1052 59072e7e Michael Hanselmann
1053 24818e8f Michael Hanselmann
def NewUUID():
1054 59072e7e Michael Hanselmann
  """Returns a random UUID.
1055 59072e7e Michael Hanselmann

1056 58885d79 Iustin Pop
  @note: This is a Linux-specific method as it uses the /proc
1057 58885d79 Iustin Pop
      filesystem.
1058 58885d79 Iustin Pop
  @rtype: str
1059 58885d79 Iustin Pop

1060 59072e7e Michael Hanselmann
  """
1061 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
1062 59072e7e Michael Hanselmann
  try:
1063 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
1064 59072e7e Michael Hanselmann
  finally:
1065 59072e7e Michael Hanselmann
    f.close()
1066 087b34fe Iustin Pop
1067 087b34fe Iustin Pop
1068 33081d90 Iustin Pop
def GenerateSecret():
1069 33081d90 Iustin Pop
  """Generates a random secret.
1070 33081d90 Iustin Pop

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

1074 58885d79 Iustin Pop
  @rtype: str
1075 58885d79 Iustin Pop
  @return: a sha1 hexdigest of a block of 64 random bytes
1076 58885d79 Iustin Pop

1077 33081d90 Iustin Pop
  """
1078 33081d90 Iustin Pop
  return sha.new(os.urandom(64)).hexdigest()
1079 33081d90 Iustin Pop
1080 33081d90 Iustin Pop
1081 ca0aa6d0 Michael Hanselmann
def ReadFile(file_name, size=None):
1082 ca0aa6d0 Michael Hanselmann
  """Reads a file.
1083 ca0aa6d0 Michael Hanselmann

1084 ca0aa6d0 Michael Hanselmann
  @type size: None or int
1085 ca0aa6d0 Michael Hanselmann
  @param size: Read at most size bytes
1086 58885d79 Iustin Pop
  @rtype: str
1087 58885d79 Iustin Pop
  @return: the (possibly partial) conent of the file
1088 ca0aa6d0 Michael Hanselmann

1089 ca0aa6d0 Michael Hanselmann
  """
1090 ca0aa6d0 Michael Hanselmann
  f = open(file_name, "r")
1091 ca0aa6d0 Michael Hanselmann
  try:
1092 ca0aa6d0 Michael Hanselmann
    if size is None:
1093 ca0aa6d0 Michael Hanselmann
      return f.read()
1094 ca0aa6d0 Michael Hanselmann
    else:
1095 ca0aa6d0 Michael Hanselmann
      return f.read(size)
1096 ca0aa6d0 Michael Hanselmann
  finally:
1097 ca0aa6d0 Michael Hanselmann
    f.close()
1098 ca0aa6d0 Michael Hanselmann
1099 ca0aa6d0 Michael Hanselmann
1100 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
1101 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
1102 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
1103 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
1104 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
1105 087b34fe Iustin Pop
  """(Over)write a file atomically.
1106 087b34fe Iustin Pop

1107 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
1108 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
1109 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
1110 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
1111 087b34fe Iustin Pop
  mtime/atime of the file.
1112 087b34fe Iustin Pop

1113 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
1114 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
1115 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
1116 087b34fe Iustin Pop
  temporary file should be removed.
1117 087b34fe Iustin Pop

1118 58885d79 Iustin Pop
  @type file_name: str
1119 58885d79 Iustin Pop
  @param file_name: the target filename
1120 58885d79 Iustin Pop
  @type fn: callable
1121 58885d79 Iustin Pop
  @param fn: content writing function, called with
1122 58885d79 Iustin Pop
      file descriptor as parameter
1123 58885d79 Iustin Pop
  @type data: sr
1124 58885d79 Iustin Pop
  @param data: contents of the file
1125 58885d79 Iustin Pop
  @type mode: int
1126 58885d79 Iustin Pop
  @param mode: file mode
1127 58885d79 Iustin Pop
  @type uid: int
1128 58885d79 Iustin Pop
  @param uid: the owner of the file
1129 58885d79 Iustin Pop
  @type gid: int
1130 58885d79 Iustin Pop
  @param gid: the group of the file
1131 58885d79 Iustin Pop
  @type atime: int
1132 58885d79 Iustin Pop
  @param atime: a custom access time to be set on the file
1133 58885d79 Iustin Pop
  @type mtime: int
1134 58885d79 Iustin Pop
  @param mtime: a custom modification time to be set on the file
1135 58885d79 Iustin Pop
  @type close: boolean
1136 58885d79 Iustin Pop
  @param close: whether to close file after writing it
1137 58885d79 Iustin Pop
  @type prewrite: callable
1138 58885d79 Iustin Pop
  @param prewrite: function to be called before writing content
1139 58885d79 Iustin Pop
  @type postwrite: callable
1140 58885d79 Iustin Pop
  @param postwrite: function to be called after writing content
1141 58885d79 Iustin Pop

1142 58885d79 Iustin Pop
  @rtype: None or int
1143 58885d79 Iustin Pop
  @return: None if the 'close' parameter evaluates to True,
1144 58885d79 Iustin Pop
      otherwise the file descriptor
1145 58885d79 Iustin Pop

1146 58885d79 Iustin Pop
  @raise errors.ProgrammerError: if an of the arguments are not valid
1147 71714516 Michael Hanselmann

1148 087b34fe Iustin Pop
  """
1149 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
1150 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1151 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
1152 087b34fe Iustin Pop
1153 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
1154 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
1155 087b34fe Iustin Pop
1156 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
1157 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
1158 087b34fe Iustin Pop
                                 " set or None")
1159 087b34fe Iustin Pop
1160 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
1161 70f4497c Michael Hanselmann
    CreateBackup(file_name)
1162 087b34fe Iustin Pop
1163 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
1164 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1165 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
1166 087b34fe Iustin Pop
  # leaves it in place
1167 087b34fe Iustin Pop
  try:
1168 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
1169 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
1170 087b34fe Iustin Pop
    if mode:
1171 087b34fe Iustin Pop
      os.chmod(new_name, mode)
1172 71714516 Michael Hanselmann
    if callable(prewrite):
1173 71714516 Michael Hanselmann
      prewrite(fd)
1174 087b34fe Iustin Pop
    if data is not None:
1175 087b34fe Iustin Pop
      os.write(fd, data)
1176 087b34fe Iustin Pop
    else:
1177 087b34fe Iustin Pop
      fn(fd)
1178 71714516 Michael Hanselmann
    if callable(postwrite):
1179 71714516 Michael Hanselmann
      postwrite(fd)
1180 087b34fe Iustin Pop
    os.fsync(fd)
1181 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
1182 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
1183 70f4497c Michael Hanselmann
    if not dry_run:
1184 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
1185 087b34fe Iustin Pop
  finally:
1186 71714516 Michael Hanselmann
    if close:
1187 71714516 Michael Hanselmann
      os.close(fd)
1188 71714516 Michael Hanselmann
      result = None
1189 71714516 Michael Hanselmann
    else:
1190 71714516 Michael Hanselmann
      result = fd
1191 087b34fe Iustin Pop
    RemoveFile(new_name)
1192 78feb6fb Guido Trotter
1193 71714516 Michael Hanselmann
  return result
1194 71714516 Michael Hanselmann
1195 78feb6fb Guido Trotter
1196 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
1197 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
1198 7b4126b7 Iustin Pop

1199 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
1200 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
1201 7b4126b7 Iustin Pop
  value, the index will be returned.
1202 7b4126b7 Iustin Pop

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

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

1208 58885d79 Iustin Pop
  @type seq: sequence
1209 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
1210 58885d79 Iustin Pop
  @type base: int
1211 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
1212 58885d79 Iustin Pop
  @rtype: int
1213 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
1214 7b4126b7 Iustin Pop

1215 7b4126b7 Iustin Pop
  """
1216 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
1217 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
1218 7b4126b7 Iustin Pop
    if elem > idx + base:
1219 7b4126b7 Iustin Pop
      # idx is not used
1220 7b4126b7 Iustin Pop
      return idx + base
1221 7b4126b7 Iustin Pop
  return None
1222 7b4126b7 Iustin Pop
1223 7b4126b7 Iustin Pop
1224 78feb6fb Guido Trotter
def all(seq, pred=bool):
1225 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
1226 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
1227 78feb6fb Guido Trotter
    return False
1228 78feb6fb Guido Trotter
  return True
1229 78feb6fb Guido Trotter
1230 78feb6fb Guido Trotter
1231 78feb6fb Guido Trotter
def any(seq, pred=bool):
1232 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
1233 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
1234 78feb6fb Guido Trotter
    return True
1235 78feb6fb Guido Trotter
  return False
1236 f7414041 Michael Hanselmann
1237 f7414041 Michael Hanselmann
1238 f7414041 Michael Hanselmann
def UniqueSequence(seq):
1239 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
1240 f7414041 Michael Hanselmann

1241 f7414041 Michael Hanselmann
  Element order is preserved.
1242 58885d79 Iustin Pop

1243 58885d79 Iustin Pop
  @type seq: sequence
1244 58885d79 Iustin Pop
  @param seq: the sequence with the source elementes
1245 58885d79 Iustin Pop
  @rtype: list
1246 58885d79 Iustin Pop
  @return: list of unique elements from seq
1247 58885d79 Iustin Pop

1248 f7414041 Michael Hanselmann
  """
1249 f7414041 Michael Hanselmann
  seen = set()
1250 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
1251 1862d460 Alexander Schreiber
1252 1862d460 Alexander Schreiber
1253 1862d460 Alexander Schreiber
def IsValidMac(mac):
1254 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
1255 1862d460 Alexander Schreiber

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

1259 58885d79 Iustin Pop
  @type mac: str
1260 58885d79 Iustin Pop
  @param mac: the MAC to be validated
1261 58885d79 Iustin Pop
  @rtype: boolean
1262 58885d79 Iustin Pop
  @return: True is the MAC seems valid
1263 58885d79 Iustin Pop

1264 1862d460 Alexander Schreiber
  """
1265 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
1266 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
1267 06009e27 Iustin Pop
1268 06009e27 Iustin Pop
1269 06009e27 Iustin Pop
def TestDelay(duration):
1270 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
1271 06009e27 Iustin Pop

1272 58885d79 Iustin Pop
  @type duration: float
1273 58885d79 Iustin Pop
  @param duration: the sleep duration
1274 58885d79 Iustin Pop
  @rtype: boolean
1275 58885d79 Iustin Pop
  @return: False for negative value, True otherwise
1276 58885d79 Iustin Pop

1277 06009e27 Iustin Pop
  """
1278 06009e27 Iustin Pop
  if duration < 0:
1279 06009e27 Iustin Pop
    return False
1280 06009e27 Iustin Pop
  time.sleep(duration)
1281 06009e27 Iustin Pop
  return True
1282 8f765069 Iustin Pop
1283 8f765069 Iustin Pop
1284 8ff612c2 Iustin Pop
def Daemonize(logfile, noclose_fds=None):
1285 8f765069 Iustin Pop
  """Daemonize the current process.
1286 8f765069 Iustin Pop

1287 8f765069 Iustin Pop
  This detaches the current process from the controlling terminal and
1288 8f765069 Iustin Pop
  runs it in the background as a daemon.
1289 8f765069 Iustin Pop

1290 58885d79 Iustin Pop
  @type logfile: str
1291 58885d79 Iustin Pop
  @param logfile: the logfile to which we should redirect stdout/stderr
1292 58885d79 Iustin Pop
  @type noclose_fds: list or None
1293 58885d79 Iustin Pop
  @param noclose_fds: if given, it denotes a list of file descriptor
1294 58885d79 Iustin Pop
      that should not be closed
1295 58885d79 Iustin Pop
  @rtype: int
1296 58885d79 Iustin Pop
  @returns: the value zero
1297 58885d79 Iustin Pop

1298 8f765069 Iustin Pop
  """
1299 8f765069 Iustin Pop
  UMASK = 077
1300 8f765069 Iustin Pop
  WORKDIR = "/"
1301 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
1302 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
1303 8f765069 Iustin Pop
    try:
1304 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
1305 8f765069 Iustin Pop
      if MAXFD < 0:
1306 8f765069 Iustin Pop
        MAXFD = 1024
1307 8f765069 Iustin Pop
    except OSError:
1308 8f765069 Iustin Pop
      MAXFD = 1024
1309 8f765069 Iustin Pop
  else:
1310 8f765069 Iustin Pop
    MAXFD = 1024
1311 8f765069 Iustin Pop
1312 8f765069 Iustin Pop
  # this might fail
1313 8f765069 Iustin Pop
  pid = os.fork()
1314 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
1315 8f765069 Iustin Pop
    os.setsid()
1316 8f765069 Iustin Pop
    # this might fail
1317 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1318 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1319 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1320 8f765069 Iustin Pop
      os.umask(UMASK)
1321 8f765069 Iustin Pop
    else:
1322 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1323 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1324 8f765069 Iustin Pop
  else:
1325 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1326 8f765069 Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1327 8f765069 Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1328 8f765069 Iustin Pop
    maxfd = MAXFD
1329 8f765069 Iustin Pop
1330 8f765069 Iustin Pop
  # Iterate through and close all file descriptors.
1331 8f765069 Iustin Pop
  for fd in range(0, maxfd):
1332 8ff612c2 Iustin Pop
    if noclose_fds and fd in noclose_fds:
1333 8ff612c2 Iustin Pop
      continue
1334 8f765069 Iustin Pop
    try:
1335 8f765069 Iustin Pop
      os.close(fd)
1336 8f765069 Iustin Pop
    except OSError: # ERROR, fd wasn't open to begin with (ignored)
1337 8f765069 Iustin Pop
      pass
1338 8f765069 Iustin Pop
  os.open(logfile, os.O_RDWR|os.O_CREAT|os.O_APPEND, 0600)
1339 8f765069 Iustin Pop
  # Duplicate standard input to standard output and standard error.
1340 8f765069 Iustin Pop
  os.dup2(0, 1)     # standard output (1)
1341 8f765069 Iustin Pop
  os.dup2(0, 2)     # standard error (2)
1342 8f765069 Iustin Pop
  return 0
1343 57c177af Iustin Pop
1344 57c177af Iustin Pop
1345 53beffbb Iustin Pop
def DaemonPidFileName(name):
1346 58885d79 Iustin Pop
  """Compute a ganeti pid file absolute path
1347 58885d79 Iustin Pop

1348 58885d79 Iustin Pop
  @type name: str
1349 58885d79 Iustin Pop
  @param name: the daemon name
1350 58885d79 Iustin Pop
  @rtype: str
1351 58885d79 Iustin Pop
  @return: the full path to the pidfile corresponding to the given
1352 58885d79 Iustin Pop
      daemon name
1353 b330ac0b Guido Trotter

1354 b330ac0b Guido Trotter
  """
1355 b330ac0b Guido Trotter
  return os.path.join(constants.RUN_GANETI_DIR, "%s.pid" % name)
1356 b330ac0b Guido Trotter
1357 b330ac0b Guido Trotter
1358 b330ac0b Guido Trotter
def WritePidFile(name):
1359 b330ac0b Guido Trotter
  """Write the current process pidfile.
1360 b330ac0b Guido Trotter

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

1363 58885d79 Iustin Pop
  @type name: str
1364 58885d79 Iustin Pop
  @param name: the daemon name to use
1365 58885d79 Iustin Pop
  @raise errors.GenericError: if the pid file already exists and
1366 58885d79 Iustin Pop
      points to a live process
1367 b330ac0b Guido Trotter

1368 b330ac0b Guido Trotter
  """
1369 b330ac0b Guido Trotter
  pid = os.getpid()
1370 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1371 d9f311d7 Iustin Pop
  if IsProcessAlive(ReadPidFile(pidfilename)):
1372 533bb4b1 Michael Hanselmann
    raise errors.GenericError("%s contains a live process" % pidfilename)
1373 b330ac0b Guido Trotter
1374 b330ac0b Guido Trotter
  WriteFile(pidfilename, data="%d\n" % pid)
1375 b330ac0b Guido Trotter
1376 b330ac0b Guido Trotter
1377 b330ac0b Guido Trotter
def RemovePidFile(name):
1378 b330ac0b Guido Trotter
  """Remove the current process pidfile.
1379 b330ac0b Guido Trotter

1380 b330ac0b Guido Trotter
  Any errors are ignored.
1381 b330ac0b Guido Trotter

1382 58885d79 Iustin Pop
  @type name: str
1383 58885d79 Iustin Pop
  @param name: the daemon name used to derive the pidfile name
1384 58885d79 Iustin Pop

1385 b330ac0b Guido Trotter
  """
1386 b330ac0b Guido Trotter
  pid = os.getpid()
1387 53beffbb Iustin Pop
  pidfilename = DaemonPidFileName(name)
1388 b330ac0b Guido Trotter
  # TODO: we could check here that the file contains our pid
1389 b330ac0b Guido Trotter
  try:
1390 b330ac0b Guido Trotter
    RemoveFile(pidfilename)
1391 b330ac0b Guido Trotter
  except:
1392 b330ac0b Guido Trotter
    pass
1393 b330ac0b Guido Trotter
1394 b330ac0b Guido Trotter
1395 ff5251bc Iustin Pop
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1396 ff5251bc Iustin Pop
                waitpid=False):
1397 b2a1f511 Iustin Pop
  """Kill a process given by its pid.
1398 b2a1f511 Iustin Pop

1399 b2a1f511 Iustin Pop
  @type pid: int
1400 b2a1f511 Iustin Pop
  @param pid: The PID to terminate.
1401 38206f3c Iustin Pop
  @type signal_: int
1402 38206f3c Iustin Pop
  @param signal_: The signal to send, by default SIGTERM
1403 b2a1f511 Iustin Pop
  @type timeout: int
1404 b2a1f511 Iustin Pop
  @param timeout: The timeout after which, if the process is still alive,
1405 b2a1f511 Iustin Pop
                  a SIGKILL will be sent. If not positive, no such checking
1406 b2a1f511 Iustin Pop
                  will be done
1407 ff5251bc Iustin Pop
  @type waitpid: boolean
1408 ff5251bc Iustin Pop
  @param waitpid: If true, we should waitpid on this process after
1409 ff5251bc Iustin Pop
      sending signals, since it's our own child and otherwise it
1410 ff5251bc Iustin Pop
      would remain as zombie
1411 b2a1f511 Iustin Pop

1412 b2a1f511 Iustin Pop
  """
1413 ff5251bc Iustin Pop
  def _helper(pid, signal_, wait):
1414 ff5251bc Iustin Pop
    """Simple helper to encapsulate the kill/waitpid sequence"""
1415 ff5251bc Iustin Pop
    os.kill(pid, signal_)
1416 ff5251bc Iustin Pop
    if wait:
1417 ff5251bc Iustin Pop
      try:
1418 ff5251bc Iustin Pop
        os.waitpid(pid, os.WNOHANG)
1419 ff5251bc Iustin Pop
      except OSError:
1420 ff5251bc Iustin Pop
        pass
1421 ff5251bc Iustin Pop
1422 b2a1f511 Iustin Pop
  if pid <= 0:
1423 b2a1f511 Iustin Pop
    # kill with pid=0 == suicide
1424 b2a1f511 Iustin Pop
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1425 b2a1f511 Iustin Pop
1426 b2a1f511 Iustin Pop
  if not IsProcessAlive(pid):
1427 b2a1f511 Iustin Pop
    return
1428 ff5251bc Iustin Pop
  _helper(pid, signal_, waitpid)
1429 b2a1f511 Iustin Pop
  if timeout <= 0:
1430 b2a1f511 Iustin Pop
    return
1431 b2a1f511 Iustin Pop
  end = time.time() + timeout
1432 b2a1f511 Iustin Pop
  while time.time() < end and IsProcessAlive(pid):
1433 b2a1f511 Iustin Pop
    time.sleep(0.1)
1434 b2a1f511 Iustin Pop
  if IsProcessAlive(pid):
1435 e1bd0072 Iustin Pop
    _helper(pid, signal.SIGKILL, waitpid)
1436 b2a1f511 Iustin Pop
1437 b2a1f511 Iustin Pop
1438 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1439 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1440 57c177af Iustin Pop

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

1444 58885d79 Iustin Pop
  @type name: str
1445 58885d79 Iustin Pop
  @param name: the name to look for
1446 58885d79 Iustin Pop
  @type search_path: str
1447 58885d79 Iustin Pop
  @param search_path: location to start at
1448 58885d79 Iustin Pop
  @type test: callable
1449 58885d79 Iustin Pop
  @param test: a function taking one argument that should return True
1450 58885d79 Iustin Pop
      if the a given object is valid; the default value is
1451 58885d79 Iustin Pop
      os.path.exists, causing only existing files to be returned
1452 58885d79 Iustin Pop
  @rtype: str or None
1453 58885d79 Iustin Pop
  @return: full path to the object if found, None otherwise
1454 57c177af Iustin Pop

1455 57c177af Iustin Pop
  """
1456 57c177af Iustin Pop
  for dir_name in search_path:
1457 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1458 57c177af Iustin Pop
    if test(item_name):
1459 57c177af Iustin Pop
      return item_name
1460 57c177af Iustin Pop
  return None
1461 8d1a2a64 Michael Hanselmann
1462 8d1a2a64 Michael Hanselmann
1463 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1464 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1465 8d1a2a64 Michael Hanselmann

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

1469 58885d79 Iustin Pop
  @type vglist: dict
1470 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
1471 58885d79 Iustin Pop
  @type vgname: str
1472 58885d79 Iustin Pop
  @param vgname: the volume group we should check
1473 58885d79 Iustin Pop
  @type minsize: int
1474 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
1475 58885d79 Iustin Pop
  @rtype: None or str
1476 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
1477 8d1a2a64 Michael Hanselmann

1478 8d1a2a64 Michael Hanselmann
  """
1479 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1480 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1481 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1482 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1483 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1484 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1485 8d1a2a64 Michael Hanselmann
  return None
1486 7996a135 Iustin Pop
1487 7996a135 Iustin Pop
1488 45bc5e4a Michael Hanselmann
def SplitTime(value):
1489 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
1490 739be818 Michael Hanselmann

1491 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
1492 45bc5e4a Michael Hanselmann
  @type value: int or float
1493 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
1494 739be818 Michael Hanselmann

1495 739be818 Michael Hanselmann
  """
1496 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1497 45bc5e4a Michael Hanselmann
1498 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1499 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1500 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1501 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1502 45bc5e4a Michael Hanselmann
1503 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
1504 739be818 Michael Hanselmann
1505 739be818 Michael Hanselmann
1506 739be818 Michael Hanselmann
def MergeTime(timetuple):
1507 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
1508 739be818 Michael Hanselmann

1509 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
1510 739be818 Michael Hanselmann
  @type timetuple: tuple
1511 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
1512 739be818 Michael Hanselmann

1513 739be818 Michael Hanselmann
  """
1514 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
1515 739be818 Michael Hanselmann
1516 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
1517 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1518 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
1519 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
1520 739be818 Michael Hanselmann
1521 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
1522 739be818 Michael Hanselmann
1523 739be818 Michael Hanselmann
1524 4a8b186a Michael Hanselmann
def GetNodeDaemonPort():
1525 4a8b186a Michael Hanselmann
  """Get the node daemon port for this cluster.
1526 4a8b186a Michael Hanselmann

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

1531 58885d79 Iustin Pop
  @rtype: int
1532 58885d79 Iustin Pop

1533 4a8b186a Michael Hanselmann
  """
1534 4a8b186a Michael Hanselmann
  try:
1535 4a8b186a Michael Hanselmann
    port = socket.getservbyname("ganeti-noded", "tcp")
1536 4a8b186a Michael Hanselmann
  except socket.error:
1537 4a8b186a Michael Hanselmann
    port = constants.DEFAULT_NODED_PORT
1538 4a8b186a Michael Hanselmann
1539 4a8b186a Michael Hanselmann
  return port
1540 4a8b186a Michael Hanselmann
1541 4a8b186a Michael Hanselmann
1542 82d9caef Iustin Pop
def SetupLogging(logfile, debug=False, stderr_logging=False, program=""):
1543 82d9caef Iustin Pop
  """Configures the logging module.
1544 82d9caef Iustin Pop

1545 58885d79 Iustin Pop
  @type logfile: str
1546 58885d79 Iustin Pop
  @param logfile: the filename to which we should log
1547 58885d79 Iustin Pop
  @type debug: boolean
1548 58885d79 Iustin Pop
  @param debug: whether to enable debug messages too or
1549 58885d79 Iustin Pop
      only those at C{INFO} and above level
1550 58885d79 Iustin Pop
  @type stderr_logging: boolean
1551 58885d79 Iustin Pop
  @param stderr_logging: whether we should also log to the standard error
1552 58885d79 Iustin Pop
  @type program: str
1553 58885d79 Iustin Pop
  @param program: the name under which we should log messages
1554 58885d79 Iustin Pop
  @raise EnvironmentError: if we can't open the log file and
1555 58885d79 Iustin Pop
      stderr logging is disabled
1556 58885d79 Iustin Pop

1557 82d9caef Iustin Pop
  """
1558 82d9caef Iustin Pop
  fmt = "%(asctime)s: " + program + " "
1559 82d9caef Iustin Pop
  if debug:
1560 82d9caef Iustin Pop
    fmt += ("pid=%(process)d/%(threadName)s %(levelname)s"
1561 82d9caef Iustin Pop
           " %(module)s:%(lineno)s %(message)s")
1562 82d9caef Iustin Pop
  else:
1563 82d9caef Iustin Pop
    fmt += "pid=%(process)d %(levelname)s %(message)s"
1564 82d9caef Iustin Pop
  formatter = logging.Formatter(fmt)
1565 82d9caef Iustin Pop
1566 82d9caef Iustin Pop
  root_logger = logging.getLogger("")
1567 82d9caef Iustin Pop
  root_logger.setLevel(logging.NOTSET)
1568 82d9caef Iustin Pop
1569 6346a9e5 Michael Hanselmann
  # Remove all previously setup handlers
1570 6346a9e5 Michael Hanselmann
  for handler in root_logger.handlers:
1571 6346a9e5 Michael Hanselmann
    root_logger.removeHandler(handler)
1572 6346a9e5 Michael Hanselmann
1573 82d9caef Iustin Pop
  if stderr_logging:
1574 82d9caef Iustin Pop
    stderr_handler = logging.StreamHandler()
1575 82d9caef Iustin Pop
    stderr_handler.setFormatter(formatter)
1576 82d9caef Iustin Pop
    if debug:
1577 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.NOTSET)
1578 82d9caef Iustin Pop
    else:
1579 82d9caef Iustin Pop
      stderr_handler.setLevel(logging.CRITICAL)
1580 82d9caef Iustin Pop
    root_logger.addHandler(stderr_handler)
1581 82d9caef Iustin Pop
1582 82d9caef Iustin Pop
  # this can fail, if the logging directories are not setup or we have
1583 82d9caef Iustin Pop
  # a permisssion problem; in this case, it's best to log but ignore
1584 82d9caef Iustin Pop
  # the error if stderr_logging is True, and if false we re-raise the
1585 82d9caef Iustin Pop
  # exception since otherwise we could run but without any logs at all
1586 82d9caef Iustin Pop
  try:
1587 82d9caef Iustin Pop
    logfile_handler = logging.FileHandler(logfile)
1588 82d9caef Iustin Pop
    logfile_handler.setFormatter(formatter)
1589 82d9caef Iustin Pop
    if debug:
1590 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.DEBUG)
1591 82d9caef Iustin Pop
    else:
1592 82d9caef Iustin Pop
      logfile_handler.setLevel(logging.INFO)
1593 82d9caef Iustin Pop
    root_logger.addHandler(logfile_handler)
1594 82d9caef Iustin Pop
  except EnvironmentError, err:
1595 82d9caef Iustin Pop
    if stderr_logging:
1596 82d9caef Iustin Pop
      logging.exception("Failed to enable logging to file '%s'", logfile)
1597 82d9caef Iustin Pop
    else:
1598 82d9caef Iustin Pop
      # we need to re-raise the exception
1599 82d9caef Iustin Pop
      raise
1600 82d9caef Iustin Pop
1601 82d9caef Iustin Pop
1602 7996a135 Iustin Pop
def LockedMethod(fn):
1603 7996a135 Iustin Pop
  """Synchronized object access decorator.
1604 7996a135 Iustin Pop

1605 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1606 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1607 7996a135 Iustin Pop

1608 7996a135 Iustin Pop
  """
1609 e67bd559 Michael Hanselmann
  def _LockDebug(*args, **kwargs):
1610 e67bd559 Michael Hanselmann
    if debug_locks:
1611 e67bd559 Michael Hanselmann
      logging.debug(*args, **kwargs)
1612 e67bd559 Michael Hanselmann
1613 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1614 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1615 7996a135 Iustin Pop
    lock = self._lock
1616 e67bd559 Michael Hanselmann
    _LockDebug("Waiting for %s", lock)
1617 7996a135 Iustin Pop
    lock.acquire()
1618 7996a135 Iustin Pop
    try:
1619 e67bd559 Michael Hanselmann
      _LockDebug("Acquired %s", lock)
1620 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1621 7996a135 Iustin Pop
    finally:
1622 e67bd559 Michael Hanselmann
      _LockDebug("Releasing %s", lock)
1623 7996a135 Iustin Pop
      lock.release()
1624 e67bd559 Michael Hanselmann
      _LockDebug("Released %s", lock)
1625 7996a135 Iustin Pop
    return result
1626 7996a135 Iustin Pop
  return wrapper
1627 eb0f0ce0 Michael Hanselmann
1628 eb0f0ce0 Michael Hanselmann
1629 eb0f0ce0 Michael Hanselmann
def LockFile(fd):
1630 eb0f0ce0 Michael Hanselmann
  """Locks a file using POSIX locks.
1631 eb0f0ce0 Michael Hanselmann

1632 58885d79 Iustin Pop
  @type fd: int
1633 58885d79 Iustin Pop
  @param fd: the file descriptor we need to lock
1634 58885d79 Iustin Pop

1635 eb0f0ce0 Michael Hanselmann
  """
1636 eb0f0ce0 Michael Hanselmann
  try:
1637 eb0f0ce0 Michael Hanselmann
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
1638 eb0f0ce0 Michael Hanselmann
  except IOError, err:
1639 eb0f0ce0 Michael Hanselmann
    if err.errno == errno.EAGAIN:
1640 eb0f0ce0 Michael Hanselmann
      raise errors.LockError("File already locked")
1641 eb0f0ce0 Michael Hanselmann
    raise
1642 de499029 Michael Hanselmann
1643 de499029 Michael Hanselmann
1644 a87b4824 Michael Hanselmann
class FileLock(object):
1645 a87b4824 Michael Hanselmann
  """Utility class for file locks.
1646 a87b4824 Michael Hanselmann

1647 a87b4824 Michael Hanselmann
  """
1648 a87b4824 Michael Hanselmann
  def __init__(self, filename):
1649 58885d79 Iustin Pop
    """Constructor for FileLock.
1650 58885d79 Iustin Pop

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

1653 58885d79 Iustin Pop
    @type filename: str
1654 58885d79 Iustin Pop
    @param filename: path to the file to be locked
1655 58885d79 Iustin Pop

1656 58885d79 Iustin Pop
    """
1657 a87b4824 Michael Hanselmann
    self.filename = filename
1658 a87b4824 Michael Hanselmann
    self.fd = open(self.filename, "w")
1659 a87b4824 Michael Hanselmann
1660 a87b4824 Michael Hanselmann
  def __del__(self):
1661 a87b4824 Michael Hanselmann
    self.Close()
1662 a87b4824 Michael Hanselmann
1663 a87b4824 Michael Hanselmann
  def Close(self):
1664 58885d79 Iustin Pop
    """Close the file and release the lock.
1665 58885d79 Iustin Pop

1666 58885d79 Iustin Pop
    """
1667 a87b4824 Michael Hanselmann
    if self.fd:
1668 a87b4824 Michael Hanselmann
      self.fd.close()
1669 a87b4824 Michael Hanselmann
      self.fd = None
1670 a87b4824 Michael Hanselmann
1671 aa74b828 Michael Hanselmann
  def _flock(self, flag, blocking, timeout, errmsg):
1672 aa74b828 Michael Hanselmann
    """Wrapper for fcntl.flock.
1673 aa74b828 Michael Hanselmann

1674 aa74b828 Michael Hanselmann
    @type flag: int
1675 58885d79 Iustin Pop
    @param flag: operation flag
1676 aa74b828 Michael Hanselmann
    @type blocking: bool
1677 58885d79 Iustin Pop
    @param blocking: whether the operation should be done in blocking mode.
1678 aa74b828 Michael Hanselmann
    @type timeout: None or float
1679 58885d79 Iustin Pop
    @param timeout: for how long the operation should be retried (implies
1680 aa74b828 Michael Hanselmann
                    non-blocking mode).
1681 aa74b828 Michael Hanselmann
    @type errmsg: string
1682 58885d79 Iustin Pop
    @param errmsg: error message in case operation fails.
1683 aa74b828 Michael Hanselmann

1684 aa74b828 Michael Hanselmann
    """
1685 a87b4824 Michael Hanselmann
    assert self.fd, "Lock was closed"
1686 aa74b828 Michael Hanselmann
    assert timeout is None or timeout >= 0, \
1687 aa74b828 Michael Hanselmann
      "If specified, timeout must be positive"
1688 a87b4824 Michael Hanselmann
1689 aa74b828 Michael Hanselmann
    if timeout is not None:
1690 a87b4824 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1691 aa74b828 Michael Hanselmann
      timeout_end = time.time() + timeout
1692 a87b4824 Michael Hanselmann
1693 aa74b828 Michael Hanselmann
    # Blocking doesn't have effect with timeout
1694 aa74b828 Michael Hanselmann
    elif not blocking:
1695 aa74b828 Michael Hanselmann
      flag |= fcntl.LOCK_NB
1696 aa74b828 Michael Hanselmann
      timeout_end = None
1697 aa74b828 Michael Hanselmann
1698 aa74b828 Michael Hanselmann
    retry = True
1699 aa74b828 Michael Hanselmann
    while retry:
1700 aa74b828 Michael Hanselmann
      try:
1701 aa74b828 Michael Hanselmann
        fcntl.flock(self.fd, flag)
1702 aa74b828 Michael Hanselmann
        retry = False
1703 aa74b828 Michael Hanselmann
      except IOError, err:
1704 aa74b828 Michael Hanselmann
        if err.errno in (errno.EAGAIN, ):
1705 aa74b828 Michael Hanselmann
          if timeout_end is not None and time.time() < timeout_end:
1706 aa74b828 Michael Hanselmann
            # Wait before trying again
1707 aa74b828 Michael Hanselmann
            time.sleep(max(0.1, min(1.0, timeout)))
1708 aa74b828 Michael Hanselmann
          else:
1709 aa74b828 Michael Hanselmann
            raise errors.LockError(errmsg)
1710 aa74b828 Michael Hanselmann
        else:
1711 aa74b828 Michael Hanselmann
          logging.exception("fcntl.flock failed")
1712 aa74b828 Michael Hanselmann
          raise
1713 aa74b828 Michael Hanselmann
1714 aa74b828 Michael Hanselmann
  def Exclusive(self, blocking=False, timeout=None):
1715 a87b4824 Michael Hanselmann
    """Locks the file in exclusive mode.
1716 a87b4824 Michael Hanselmann

1717 58885d79 Iustin Pop
    @type blocking: boolean
1718 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1719 58885d79 Iustin Pop
        can lock the file or return immediately
1720 58885d79 Iustin Pop
    @type timeout: int or None
1721 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1722 58885d79 Iustin Pop
        (in blocking mode)
1723 58885d79 Iustin Pop

1724 a87b4824 Michael Hanselmann
    """
1725 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_EX, blocking, timeout,
1726 a87b4824 Michael Hanselmann
                "Failed to lock %s in exclusive mode" % self.filename)
1727 a87b4824 Michael Hanselmann
1728 aa74b828 Michael Hanselmann
  def Shared(self, blocking=False, timeout=None):
1729 a87b4824 Michael Hanselmann
    """Locks the file in shared mode.
1730 a87b4824 Michael Hanselmann

1731 58885d79 Iustin Pop
    @type blocking: boolean
1732 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1733 58885d79 Iustin Pop
        can lock the file or return immediately
1734 58885d79 Iustin Pop
    @type timeout: int or None
1735 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1736 58885d79 Iustin Pop
        (in blocking mode)
1737 58885d79 Iustin Pop

1738 a87b4824 Michael Hanselmann
    """
1739 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_SH, blocking, timeout,
1740 a87b4824 Michael Hanselmann
                "Failed to lock %s in shared mode" % self.filename)
1741 a87b4824 Michael Hanselmann
1742 aa74b828 Michael Hanselmann
  def Unlock(self, blocking=True, timeout=None):
1743 a87b4824 Michael Hanselmann
    """Unlocks the file.
1744 a87b4824 Michael Hanselmann

1745 58885d79 Iustin Pop
    According to C{flock(2)}, unlocking can also be a nonblocking
1746 58885d79 Iustin Pop
    operation::
1747 58885d79 Iustin Pop

1748 58885d79 Iustin Pop
      To make a non-blocking request, include LOCK_NB with any of the above
1749 58885d79 Iustin Pop
      operations.
1750 58885d79 Iustin Pop

1751 58885d79 Iustin Pop
    @type blocking: boolean
1752 58885d79 Iustin Pop
    @param blocking: whether to block and wait until we
1753 58885d79 Iustin Pop
        can lock the file or return immediately
1754 58885d79 Iustin Pop
    @type timeout: int or None
1755 58885d79 Iustin Pop
    @param timeout: if not None, the duration to wait for the lock
1756 58885d79 Iustin Pop
        (in blocking mode)
1757 a87b4824 Michael Hanselmann

1758 a87b4824 Michael Hanselmann
    """
1759 aa74b828 Michael Hanselmann
    self._flock(fcntl.LOCK_UN, blocking, timeout,
1760 a87b4824 Michael Hanselmann
                "Failed to unlock %s" % self.filename)
1761 a87b4824 Michael Hanselmann
1762 a87b4824 Michael Hanselmann
1763 de499029 Michael Hanselmann
class SignalHandler(object):
1764 de499029 Michael Hanselmann
  """Generic signal handler class.
1765 de499029 Michael Hanselmann

1766 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
1767 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
1768 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
1769 58885d79 Iustin Pop
  signal was sent.
1770 58885d79 Iustin Pop

1771 58885d79 Iustin Pop
  @type signum: list
1772 58885d79 Iustin Pop
  @ivar signum: the signals we handle
1773 58885d79 Iustin Pop
  @type called: boolean
1774 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
1775 de499029 Michael Hanselmann

1776 de499029 Michael Hanselmann
  """
1777 de499029 Michael Hanselmann
  def __init__(self, signum):
1778 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
1779 de499029 Michael Hanselmann

1780 58885d79 Iustin Pop
    @type signum: int or list of ints
1781 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
1782 de499029 Michael Hanselmann

1783 de499029 Michael Hanselmann
    """
1784 de499029 Michael Hanselmann
    if isinstance(signum, (int, long)):
1785 de499029 Michael Hanselmann
      self.signum = set([signum])
1786 de499029 Michael Hanselmann
    else:
1787 de499029 Michael Hanselmann
      self.signum = set(signum)
1788 de499029 Michael Hanselmann
1789 de499029 Michael Hanselmann
    self.called = False
1790 de499029 Michael Hanselmann
1791 de499029 Michael Hanselmann
    self._previous = {}
1792 de499029 Michael Hanselmann
    try:
1793 de499029 Michael Hanselmann
      for signum in self.signum:
1794 de499029 Michael Hanselmann
        # Setup handler
1795 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
1796 de499029 Michael Hanselmann
        try:
1797 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
1798 de499029 Michael Hanselmann
        except:
1799 de499029 Michael Hanselmann
          # Restore previous handler
1800 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
1801 de499029 Michael Hanselmann
          raise
1802 de499029 Michael Hanselmann
    except:
1803 de499029 Michael Hanselmann
      # Reset all handlers
1804 de499029 Michael Hanselmann
      self.Reset()
1805 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
1806 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
1807 de499029 Michael Hanselmann
      raise
1808 de499029 Michael Hanselmann
1809 de499029 Michael Hanselmann
  def __del__(self):
1810 de499029 Michael Hanselmann
    self.Reset()
1811 de499029 Michael Hanselmann
1812 de499029 Michael Hanselmann
  def Reset(self):
1813 de499029 Michael Hanselmann
    """Restore previous handler.
1814 de499029 Michael Hanselmann

1815 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
1816 58885d79 Iustin Pop

1817 de499029 Michael Hanselmann
    """
1818 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
1819 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
1820 de499029 Michael Hanselmann
      # If successful, remove from dict
1821 de499029 Michael Hanselmann
      del self._previous[signum]
1822 de499029 Michael Hanselmann
1823 de499029 Michael Hanselmann
  def Clear(self):
1824 58885d79 Iustin Pop
    """Unsets the L{called} flag.
1825 de499029 Michael Hanselmann

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

1828 de499029 Michael Hanselmann
    """
1829 de499029 Michael Hanselmann
    self.called = False
1830 de499029 Michael Hanselmann
1831 de499029 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
1832 de499029 Michael Hanselmann
    """Actual signal handling function.
1833 de499029 Michael Hanselmann

1834 de499029 Michael Hanselmann
    """
1835 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
1836 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
1837 de499029 Michael Hanselmann
    self.called = True
1838 a2d2e1a7 Iustin Pop
1839 a2d2e1a7 Iustin Pop
1840 a2d2e1a7 Iustin Pop
class FieldSet(object):
1841 a2d2e1a7 Iustin Pop
  """A simple field set.
1842 a2d2e1a7 Iustin Pop

1843 a2d2e1a7 Iustin Pop
  Among the features are:
1844 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
1845 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
1846 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
1847 a2d2e1a7 Iustin Pop

1848 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
1849 a2d2e1a7 Iustin Pop

1850 a2d2e1a7 Iustin Pop
  """
1851 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
1852 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
1853 a2d2e1a7 Iustin Pop
1854 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
1855 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
1856 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
1857 a2d2e1a7 Iustin Pop
1858 a2d2e1a7 Iustin Pop
  def Matches(self, field):
1859 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
1860 a2d2e1a7 Iustin Pop

1861 a2d2e1a7 Iustin Pop
    @type field: str
1862 a2d2e1a7 Iustin Pop
    @param field: the string to match
1863 a2d2e1a7 Iustin Pop
    @return: either False or a regular expression match object
1864 a2d2e1a7 Iustin Pop

1865 a2d2e1a7 Iustin Pop
    """
1866 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
1867 a2d2e1a7 Iustin Pop
      return m
1868 a2d2e1a7 Iustin Pop
    return False
1869 a2d2e1a7 Iustin Pop
1870 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
1871 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
1872 a2d2e1a7 Iustin Pop

1873 a2d2e1a7 Iustin Pop
    @type items: list
1874 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
1875 a2d2e1a7 Iustin Pop
    @rtype: list
1876 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
1877 a2d2e1a7 Iustin Pop

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