Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ c8a0948f

History | View | Annotate | Download (24.6 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 a8083063 Iustin Pop
"""Ganeti small utilities
23 899d2a81 Michael Hanselmann

24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
27 a8083063 Iustin Pop
import sys
28 a8083063 Iustin Pop
import os
29 a8083063 Iustin Pop
import sha
30 a8083063 Iustin Pop
import time
31 113b55aa Iustin Pop
import subprocess
32 a8083063 Iustin Pop
import re
33 a8083063 Iustin Pop
import socket
34 a8083063 Iustin Pop
import tempfile
35 a8083063 Iustin Pop
import shutil
36 4ca1b175 Alexander Schreiber
import errno
37 2f8b60b3 Iustin Pop
import pwd
38 78feb6fb Guido Trotter
import itertools
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
from ganeti import logger
41 a8083063 Iustin Pop
from ganeti import errors
42 a8083063 Iustin Pop
43 16abfbc2 Alexander Schreiber
44 a8083063 Iustin Pop
_locksheld = []
45 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
46 a8083063 Iustin Pop
47 a8083063 Iustin Pop
class RunResult(object):
48 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
49 a8083063 Iustin Pop

50 a8083063 Iustin Pop
  Instance variables:
51 a8083063 Iustin Pop
    exit_code: the exit code of the program, or None (if the program
52 a8083063 Iustin Pop
               didn't exit())
53 a8083063 Iustin Pop
    signal: numeric signal that caused the program to finish, or None
54 a8083063 Iustin Pop
            (if the program wasn't terminated by a signal)
55 a8083063 Iustin Pop
    stdout: the standard output of the program
56 a8083063 Iustin Pop
    stderr: the standard error of the program
57 a8083063 Iustin Pop
    failed: a Boolean value which is True in case the program was
58 a8083063 Iustin Pop
            terminated by a signal or exited with a non-zero exit code
59 a8083063 Iustin Pop
    fail_reason: a string detailing the termination reason
60 a8083063 Iustin Pop

61 a8083063 Iustin Pop
  """
62 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
63 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
64 a8083063 Iustin Pop
65 a8083063 Iustin Pop
66 a8083063 Iustin Pop
  def __init__(self, exit_code, signal, stdout, stderr, cmd):
67 a8083063 Iustin Pop
    self.cmd = cmd
68 a8083063 Iustin Pop
    self.exit_code = exit_code
69 a8083063 Iustin Pop
    self.signal = signal
70 a8083063 Iustin Pop
    self.stdout = stdout
71 a8083063 Iustin Pop
    self.stderr = stderr
72 a8083063 Iustin Pop
    self.failed = (signal is not None or exit_code != 0)
73 a8083063 Iustin Pop
74 a8083063 Iustin Pop
    if self.signal is not None:
75 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
76 a8083063 Iustin Pop
    elif self.exit_code is not None:
77 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
78 a8083063 Iustin Pop
    else:
79 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
80 a8083063 Iustin Pop
81 a8083063 Iustin Pop
  def _GetOutput(self):
82 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
83 a8083063 Iustin Pop

84 a8083063 Iustin Pop
    """
85 a8083063 Iustin Pop
    return self.stdout + self.stderr
86 a8083063 Iustin Pop
87 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
def _GetLockFile(subsystem):
91 a8083063 Iustin Pop
  """Compute the file name for a given lock name."""
92 a8083063 Iustin Pop
  return "/var/lock/ganeti_lock_%s" % subsystem
93 a8083063 Iustin Pop
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
def Lock(name, max_retries=None, debug=False):
96 a8083063 Iustin Pop
  """Lock a given subsystem.
97 a8083063 Iustin Pop

98 a8083063 Iustin Pop
  In case the lock is already held by an alive process, the function
99 a8083063 Iustin Pop
  will sleep indefintely and poll with a one second interval.
100 a8083063 Iustin Pop

101 a8083063 Iustin Pop
  When the optional integer argument 'max_retries' is passed with a
102 a8083063 Iustin Pop
  non-zero value, the function will sleep only for this number of
103 a8083063 Iustin Pop
  times, and then it will will raise a LockError if the lock can't be
104 a8083063 Iustin Pop
  acquired. Passing in a negative number will cause only one try to
105 a8083063 Iustin Pop
  get the lock. Passing a positive number will make the function retry
106 a8083063 Iustin Pop
  for approximately that number of seconds.
107 a8083063 Iustin Pop

108 a8083063 Iustin Pop
  """
109 a8083063 Iustin Pop
  lockfile = _GetLockFile(name)
110 a8083063 Iustin Pop
111 a8083063 Iustin Pop
  if name in _locksheld:
112 a8083063 Iustin Pop
    raise errors.LockError('Lock "%s" already held!' % (name,))
113 a8083063 Iustin Pop
114 a8083063 Iustin Pop
  errcount = 0
115 a8083063 Iustin Pop
116 a8083063 Iustin Pop
  retries = 0
117 a8083063 Iustin Pop
  while True:
118 a8083063 Iustin Pop
    try:
119 a8083063 Iustin Pop
      fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_SYNC)
120 a8083063 Iustin Pop
      break
121 a8083063 Iustin Pop
    except OSError, creat_err:
122 4ca1b175 Alexander Schreiber
      if creat_err.errno != errno.EEXIST:
123 3ecf6786 Iustin Pop
        raise errors.LockError("Can't create the lock file. Error '%s'." %
124 3ecf6786 Iustin Pop
                               str(creat_err))
125 a8083063 Iustin Pop
126 a8083063 Iustin Pop
      try:
127 a8083063 Iustin Pop
        pf = open(lockfile, 'r')
128 a8083063 Iustin Pop
      except IOError, open_err:
129 a8083063 Iustin Pop
        errcount += 1
130 a8083063 Iustin Pop
        if errcount >= 5:
131 3ecf6786 Iustin Pop
          raise errors.LockError("Lock file exists but cannot be opened."
132 3ecf6786 Iustin Pop
                                 " Error: '%s'." % str(open_err))
133 a8083063 Iustin Pop
        time.sleep(1)
134 a8083063 Iustin Pop
        continue
135 a8083063 Iustin Pop
136 a8083063 Iustin Pop
      try:
137 a8083063 Iustin Pop
        pid = int(pf.read())
138 a8083063 Iustin Pop
      except ValueError:
139 3ecf6786 Iustin Pop
        raise errors.LockError("Invalid pid string in %s" %
140 a8083063 Iustin Pop
                               (lockfile,))
141 a8083063 Iustin Pop
142 a8083063 Iustin Pop
      if not IsProcessAlive(pid):
143 3ecf6786 Iustin Pop
        raise errors.LockError("Stale lockfile %s for pid %d?" %
144 3ecf6786 Iustin Pop
                               (lockfile, pid))
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
      if max_retries and max_retries <= retries:
147 3ecf6786 Iustin Pop
        raise errors.LockError("Can't acquire lock during the specified"
148 3ecf6786 Iustin Pop
                               " time, aborting.")
149 a8083063 Iustin Pop
      if retries == 5 and (debug or sys.stdin.isatty()):
150 a8083063 Iustin Pop
        logger.ToStderr("Waiting for '%s' lock from pid %d..." % (name, pid))
151 a8083063 Iustin Pop
152 a8083063 Iustin Pop
      time.sleep(1)
153 a8083063 Iustin Pop
      retries += 1
154 a8083063 Iustin Pop
      continue
155 a8083063 Iustin Pop
156 a8083063 Iustin Pop
  os.write(fd, '%d\n' % (os.getpid(),))
157 a8083063 Iustin Pop
  os.close(fd)
158 a8083063 Iustin Pop
159 a8083063 Iustin Pop
  _locksheld.append(name)
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
def Unlock(name):
163 098c0958 Michael Hanselmann
  """Unlock a given subsystem.
164 a8083063 Iustin Pop

165 098c0958 Michael Hanselmann
  """
166 a8083063 Iustin Pop
  lockfile = _GetLockFile(name)
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
  try:
169 a8083063 Iustin Pop
    fd = os.open(lockfile, os.O_RDONLY)
170 a8083063 Iustin Pop
  except OSError:
171 a8083063 Iustin Pop
    raise errors.LockError('Lock "%s" not held.' % (name,))
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
  f = os.fdopen(fd, 'r')
174 a8083063 Iustin Pop
  pid_str = f.read()
175 a8083063 Iustin Pop
176 a8083063 Iustin Pop
  try:
177 a8083063 Iustin Pop
    pid = int(pid_str)
178 a8083063 Iustin Pop
  except ValueError:
179 a8083063 Iustin Pop
    raise errors.LockError('Unable to determine PID of locking process.')
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
  if pid != os.getpid():
182 a8083063 Iustin Pop
    raise errors.LockError('Lock not held by me (%d != %d)' %
183 a8083063 Iustin Pop
                           (os.getpid(), pid,))
184 a8083063 Iustin Pop
185 a8083063 Iustin Pop
  os.unlink(lockfile)
186 a8083063 Iustin Pop
  _locksheld.remove(name)
187 a8083063 Iustin Pop
188 a8083063 Iustin Pop
189 a8083063 Iustin Pop
def LockCleanup():
190 098c0958 Michael Hanselmann
  """Remove all locks.
191 a8083063 Iustin Pop

192 098c0958 Michael Hanselmann
  """
193 a8083063 Iustin Pop
  for lock in _locksheld:
194 a8083063 Iustin Pop
    Unlock(lock)
195 a8083063 Iustin Pop
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
def RunCmd(cmd):
198 a8083063 Iustin Pop
  """Execute a (shell) command.
199 a8083063 Iustin Pop

200 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
201 a8083063 Iustin Pop
  closed.
202 a8083063 Iustin Pop

203 a8083063 Iustin Pop
  Args:
204 a8083063 Iustin Pop
    cmd: command to run. (str)
205 a8083063 Iustin Pop

206 a8083063 Iustin Pop
  Returns: `RunResult` instance
207 a8083063 Iustin Pop

208 a8083063 Iustin Pop
  """
209 a8083063 Iustin Pop
  if isinstance(cmd, list):
210 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
211 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
212 113b55aa Iustin Pop
    shell = False
213 113b55aa Iustin Pop
  else:
214 113b55aa Iustin Pop
    strcmd = cmd
215 113b55aa Iustin Pop
    shell = True
216 23f41a3e Michael Hanselmann
  env = os.environ.copy()
217 23f41a3e Michael Hanselmann
  env["LC_ALL"] = "C"
218 113b55aa Iustin Pop
  child = subprocess.Popen(cmd, shell=shell,
219 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
220 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
221 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
222 23f41a3e Michael Hanselmann
                           close_fds=True, env=env)
223 113b55aa Iustin Pop
224 113b55aa Iustin Pop
  child.stdin.close()
225 113b55aa Iustin Pop
  out = child.stdout.read()
226 113b55aa Iustin Pop
  err = child.stderr.read()
227 a8083063 Iustin Pop
228 a8083063 Iustin Pop
  status = child.wait()
229 113b55aa Iustin Pop
  if status >= 0:
230 113b55aa Iustin Pop
    exitcode = status
231 a8083063 Iustin Pop
    signal = None
232 a8083063 Iustin Pop
  else:
233 a8083063 Iustin Pop
    exitcode = None
234 113b55aa Iustin Pop
    signal = -status
235 a8083063 Iustin Pop
236 a8083063 Iustin Pop
  return RunResult(exitcode, signal, out, err, strcmd)
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
239 a8083063 Iustin Pop
def RunCmdUnlocked(cmd):
240 a8083063 Iustin Pop
  """Execute a shell command without the 'cmd' lock.
241 a8083063 Iustin Pop

242 a8083063 Iustin Pop
  This variant of `RunCmd()` drops the 'cmd' lock before running the
243 a8083063 Iustin Pop
  command and re-aquires it afterwards, thus it can be used to call
244 a8083063 Iustin Pop
  other ganeti commands.
245 a8083063 Iustin Pop

246 a8083063 Iustin Pop
  The argument and return values are the same as for the `RunCmd()`
247 a8083063 Iustin Pop
  function.
248 a8083063 Iustin Pop

249 a8083063 Iustin Pop
  Args:
250 a8083063 Iustin Pop
    cmd - command to run. (str)
251 a8083063 Iustin Pop

252 a8083063 Iustin Pop
  Returns:
253 a8083063 Iustin Pop
    `RunResult`
254 a8083063 Iustin Pop

255 a8083063 Iustin Pop
  """
256 a8083063 Iustin Pop
  Unlock('cmd')
257 a8083063 Iustin Pop
  ret = RunCmd(cmd)
258 a8083063 Iustin Pop
  Lock('cmd')
259 a8083063 Iustin Pop
260 a8083063 Iustin Pop
  return ret
261 a8083063 Iustin Pop
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
def RemoveFile(filename):
264 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
265 a8083063 Iustin Pop

266 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
267 a8083063 Iustin Pop
  errors are passed.
268 a8083063 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 a8083063 Iustin Pop
  Args:
284 a8083063 Iustin Pop
    filename - Filename (str)
285 a8083063 Iustin Pop

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

306 a8083063 Iustin Pop
  Args:
307 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
308 a8083063 Iustin Pop

309 a8083063 Iustin Pop
  Return value:
310 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
311 a8083063 Iustin Pop

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

326 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
327 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
328 a8083063 Iustin Pop
  from template.
329 a8083063 Iustin Pop

330 a8083063 Iustin Pop
  Args:
331 a8083063 Iustin Pop
    target   - the dictionary to check
332 a8083063 Iustin Pop
    template - template dictionary
333 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
334 a8083063 Iustin Pop
               entry; if None, no logging will be done
335 a8083063 Iustin Pop

336 a8083063 Iustin Pop
  Returns value:
337 a8083063 Iustin Pop
    None
338 a8083063 Iustin Pop

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

354 a8083063 Iustin Pop
  Returns: true or false, depending on if the pid exists or not
355 a8083063 Iustin Pop

356 a8083063 Iustin Pop
  Remarks: zombie processes treated as not alive
357 a8083063 Iustin Pop

358 a8083063 Iustin Pop
  """
359 a8083063 Iustin Pop
  try:
360 a8083063 Iustin Pop
    f = open("/proc/%d/status" % pid)
361 a8083063 Iustin Pop
  except IOError, err:
362 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
363 a8083063 Iustin Pop
      return False
364 a8083063 Iustin Pop
365 a8083063 Iustin Pop
  alive = True
366 a8083063 Iustin Pop
  try:
367 a8083063 Iustin Pop
    data = f.readlines()
368 a8083063 Iustin Pop
    if len(data) > 1:
369 a8083063 Iustin Pop
      state = data[1].split()
370 a8083063 Iustin Pop
      if len(state) > 1 and state[1] == "Z":
371 a8083063 Iustin Pop
        alive = False
372 a8083063 Iustin Pop
  finally:
373 a8083063 Iustin Pop
    f.close()
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
  return alive
376 a8083063 Iustin Pop
377 a8083063 Iustin Pop
378 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
379 a8083063 Iustin Pop
  """Try to match a name against a list.
380 a8083063 Iustin Pop

381 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
382 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
383 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
384 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
385 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
386 a8083063 Iustin Pop

387 a8083063 Iustin Pop
  Args:
388 a8083063 Iustin Pop
    key: the name to be searched
389 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
390 a8083063 Iustin Pop

391 a8083063 Iustin Pop
  Returns:
392 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
393 a8083063 Iustin Pop
    otherwise the element from the list which matches
394 a8083063 Iustin Pop

395 a8083063 Iustin Pop
  """
396 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
397 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
398 a8083063 Iustin Pop
  if len(names_filtered) != 1:
399 a8083063 Iustin Pop
    return None
400 a8083063 Iustin Pop
  return names_filtered[0]
401 a8083063 Iustin Pop
402 a8083063 Iustin Pop
403 bcf043c9 Iustin Pop
class HostInfo:
404 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
405 bcf043c9 Iustin Pop

406 bcf043c9 Iustin Pop
  """
407 89e1fc26 Iustin Pop
  def __init__(self, name=None):
408 bcf043c9 Iustin Pop
    """Initialize the host name object.
409 bcf043c9 Iustin Pop

410 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
411 89e1fc26 Iustin Pop
    name.
412 bcf043c9 Iustin Pop

413 bcf043c9 Iustin Pop
    """
414 89e1fc26 Iustin Pop
    if name is None:
415 89e1fc26 Iustin Pop
      name = self.SysName()
416 89e1fc26 Iustin Pop
417 89e1fc26 Iustin Pop
    self.query = name
418 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
419 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
420 bcf043c9 Iustin Pop
421 c8a0948f Michael Hanselmann
  def ShortName(self):
422 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
423 c8a0948f Michael Hanselmann

424 c8a0948f Michael Hanselmann
    """
425 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
426 c8a0948f Michael Hanselmann
427 89e1fc26 Iustin Pop
  @staticmethod
428 89e1fc26 Iustin Pop
  def SysName():
429 89e1fc26 Iustin Pop
    """Return the current system's name.
430 bcf043c9 Iustin Pop

431 89e1fc26 Iustin Pop
    This is simply a wrapper over socket.gethostname()
432 a8083063 Iustin Pop

433 89e1fc26 Iustin Pop
    """
434 89e1fc26 Iustin Pop
    return socket.gethostname()
435 a8083063 Iustin Pop
436 89e1fc26 Iustin Pop
  @staticmethod
437 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
438 89e1fc26 Iustin Pop
    """Look up hostname
439 a8083063 Iustin Pop

440 89e1fc26 Iustin Pop
    Args:
441 89e1fc26 Iustin Pop
      hostname: hostname to look up
442 89e1fc26 Iustin Pop

443 89e1fc26 Iustin Pop
    Returns:
444 89e1fc26 Iustin Pop
      a tuple (name, aliases, ipaddrs) as returned by socket.gethostbyname_ex
445 89e1fc26 Iustin Pop
      in case of errors in resolving, we raise a ResolverError
446 89e1fc26 Iustin Pop

447 89e1fc26 Iustin Pop
    """
448 89e1fc26 Iustin Pop
    try:
449 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
450 89e1fc26 Iustin Pop
    except socket.gaierror, err:
451 89e1fc26 Iustin Pop
      # hostname not found in DNS
452 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
453 a8083063 Iustin Pop
454 89e1fc26 Iustin Pop
    return result
455 a8083063 Iustin Pop
456 a8083063 Iustin Pop
457 a8083063 Iustin Pop
def ListVolumeGroups():
458 a8083063 Iustin Pop
  """List volume groups and their size
459 a8083063 Iustin Pop

460 a8083063 Iustin Pop
  Returns:
461 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
462 a8083063 Iustin Pop

463 a8083063 Iustin Pop
  """
464 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
465 a8083063 Iustin Pop
  result = RunCmd(command)
466 a8083063 Iustin Pop
  retval = {}
467 a8083063 Iustin Pop
  if result.failed:
468 a8083063 Iustin Pop
    return retval
469 a8083063 Iustin Pop
470 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
471 a8083063 Iustin Pop
    try:
472 a8083063 Iustin Pop
      name, size = line.split()
473 a8083063 Iustin Pop
      size = int(float(size))
474 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
475 a8083063 Iustin Pop
      logger.Error("Invalid output from vgs (%s): %s" % (err, line))
476 a8083063 Iustin Pop
      continue
477 a8083063 Iustin Pop
478 a8083063 Iustin Pop
    retval[name] = size
479 a8083063 Iustin Pop
480 a8083063 Iustin Pop
  return retval
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
483 a8083063 Iustin Pop
def BridgeExists(bridge):
484 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
485 a8083063 Iustin Pop

486 a8083063 Iustin Pop
  Returns:
487 a8083063 Iustin Pop
     True if it does, false otherwise.
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
  """
490 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
493 a8083063 Iustin Pop
def NiceSort(name_list):
494 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
495 a8083063 Iustin Pop

496 a8083063 Iustin Pop
  Given a list of names ['a1', 'a10', 'a11', 'a2'] this function will
497 a8083063 Iustin Pop
  sort the list in the logical order ['a1', 'a2', 'a10', 'a11'].
498 a8083063 Iustin Pop

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

503 a8083063 Iustin Pop
  Return value
504 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
505 a8083063 Iustin Pop

506 a8083063 Iustin Pop
  """
507 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
508 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
509 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
510 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
511 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
512 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
513 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
514 a8083063 Iustin Pop
  def _TryInt(val):
515 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
516 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
517 a8083063 Iustin Pop
      return val
518 a8083063 Iustin Pop
    rval = int(val)
519 a8083063 Iustin Pop
    return rval
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
522 a8083063 Iustin Pop
             for name in name_list]
523 a8083063 Iustin Pop
  to_sort.sort()
524 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
525 a8083063 Iustin Pop
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
def CheckDaemonAlive(pid_file, process_string):
528 a8083063 Iustin Pop
  """Check wether the specified daemon is alive.
529 a8083063 Iustin Pop

530 a8083063 Iustin Pop
  Args:
531 a8083063 Iustin Pop
   - pid_file: file to read the daemon pid from, the file is
532 a8083063 Iustin Pop
               expected to contain only a single line containing
533 a8083063 Iustin Pop
               only the PID
534 a8083063 Iustin Pop
   - process_string: a substring that we expect to find in
535 a8083063 Iustin Pop
                     the command line of the daemon process
536 a8083063 Iustin Pop

537 a8083063 Iustin Pop
  Returns:
538 a8083063 Iustin Pop
   - True if the daemon is judged to be alive (that is:
539 a8083063 Iustin Pop
      - the PID file exists, is readable and contains a number
540 a8083063 Iustin Pop
      - a process of the specified PID is running
541 a8083063 Iustin Pop
      - that process contains the specified string in its
542 a8083063 Iustin Pop
        command line
543 a8083063 Iustin Pop
      - the process is not in state Z (zombie))
544 a8083063 Iustin Pop
   - False otherwise
545 a8083063 Iustin Pop

546 a8083063 Iustin Pop
  """
547 a8083063 Iustin Pop
  try:
548 a8083063 Iustin Pop
    pid_file = file(pid_file, 'r')
549 a8083063 Iustin Pop
    try:
550 a8083063 Iustin Pop
      pid = int(pid_file.readline())
551 a8083063 Iustin Pop
    finally:
552 a8083063 Iustin Pop
      pid_file.close()
553 a8083063 Iustin Pop
554 a8083063 Iustin Pop
    cmdline_file_path = "/proc/%s/cmdline" % (pid)
555 a8083063 Iustin Pop
    cmdline_file = open(cmdline_file_path, 'r')
556 a8083063 Iustin Pop
    try:
557 a8083063 Iustin Pop
      cmdline = cmdline_file.readline()
558 a8083063 Iustin Pop
    finally:
559 a8083063 Iustin Pop
      cmdline_file.close()
560 a8083063 Iustin Pop
561 a8083063 Iustin Pop
    if not process_string in cmdline:
562 a8083063 Iustin Pop
      return False
563 a8083063 Iustin Pop
564 a8083063 Iustin Pop
    stat_file_path =  "/proc/%s/stat" % (pid)
565 a8083063 Iustin Pop
    stat_file = open(stat_file_path, 'r')
566 a8083063 Iustin Pop
    try:
567 a8083063 Iustin Pop
      process_state = stat_file.readline().split()[2]
568 a8083063 Iustin Pop
    finally:
569 a8083063 Iustin Pop
      stat_file.close()
570 a8083063 Iustin Pop
571 a8083063 Iustin Pop
    if process_state == 'Z':
572 a8083063 Iustin Pop
      return False
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
  except (IndexError, IOError, ValueError):
575 a8083063 Iustin Pop
    return False
576 a8083063 Iustin Pop
577 a8083063 Iustin Pop
  return True
578 a8083063 Iustin Pop
579 a8083063 Iustin Pop
580 a8083063 Iustin Pop
def TryConvert(fn, val):
581 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
582 a8083063 Iustin Pop

583 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
584 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
585 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
586 a8083063 Iustin Pop
  are propagated to the caller.
587 a8083063 Iustin Pop

588 a8083063 Iustin Pop
  """
589 a8083063 Iustin Pop
  try:
590 a8083063 Iustin Pop
    nv = fn(val)
591 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
592 a8083063 Iustin Pop
    nv = val
593 a8083063 Iustin Pop
  return nv
594 a8083063 Iustin Pop
595 a8083063 Iustin Pop
596 a8083063 Iustin Pop
def IsValidIP(ip):
597 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
598 a8083063 Iustin Pop

599 a8083063 Iustin Pop
  This function checks if the ip address passes is valid or not based
600 a8083063 Iustin Pop
  on syntax (not ip range, class calculations or anything).
601 a8083063 Iustin Pop

602 a8083063 Iustin Pop
  """
603 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
604 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
605 a8083063 Iustin Pop
606 a8083063 Iustin Pop
607 a8083063 Iustin Pop
def IsValidShellParam(word):
608 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
609 a8083063 Iustin Pop

610 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
611 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
612 a8083063 Iustin Pop
  the actual command.
613 a8083063 Iustin Pop

614 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
615 a8083063 Iustin Pop
  side.
616 a8083063 Iustin Pop

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

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

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

640 a8083063 Iustin Pop
  Value needs to be passed as a numeric type. Return value is always a string.
641 a8083063 Iustin Pop

642 a8083063 Iustin Pop
  """
643 a8083063 Iustin Pop
  if value < 1024:
644 a8083063 Iustin Pop
    return "%dM" % round(value, 0)
645 a8083063 Iustin Pop
646 a8083063 Iustin Pop
  elif value < (1024 * 1024):
647 a8083063 Iustin Pop
    return "%0.1fG" % round(float(value) / 1024, 1)
648 a8083063 Iustin Pop
649 a8083063 Iustin Pop
  else:
650 a8083063 Iustin Pop
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
651 a8083063 Iustin Pop
652 a8083063 Iustin Pop
653 a8083063 Iustin Pop
def ParseUnit(input_string):
654 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
655 a8083063 Iustin Pop

656 a8083063 Iustin Pop
  Input must be in the format NUMBER+ [DOT NUMBER+] SPACE* [UNIT]. If no unit
657 a8083063 Iustin Pop
  is specified, it defaults to MiB. Return value is always an int in MiB.
658 a8083063 Iustin Pop

659 a8083063 Iustin Pop
  """
660 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
661 a8083063 Iustin Pop
  if not m:
662 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
663 a8083063 Iustin Pop
664 a8083063 Iustin Pop
  value = float(m.groups()[0])
665 a8083063 Iustin Pop
666 a8083063 Iustin Pop
  unit = m.groups()[1]
667 a8083063 Iustin Pop
  if unit:
668 a8083063 Iustin Pop
    lcunit = unit.lower()
669 a8083063 Iustin Pop
  else:
670 a8083063 Iustin Pop
    lcunit = 'm'
671 a8083063 Iustin Pop
672 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
673 a8083063 Iustin Pop
    # Value already in MiB
674 a8083063 Iustin Pop
    pass
675 a8083063 Iustin Pop
676 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
677 a8083063 Iustin Pop
    value *= 1024
678 a8083063 Iustin Pop
679 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
680 a8083063 Iustin Pop
    value *= 1024 * 1024
681 a8083063 Iustin Pop
682 a8083063 Iustin Pop
  else:
683 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
684 a8083063 Iustin Pop
685 a8083063 Iustin Pop
  # Make sure we round up
686 a8083063 Iustin Pop
  if int(value) < value:
687 a8083063 Iustin Pop
    value += 1
688 a8083063 Iustin Pop
689 a8083063 Iustin Pop
  # Round up to the next multiple of 4
690 a8083063 Iustin Pop
  value = int(value)
691 a8083063 Iustin Pop
  if value % 4:
692 a8083063 Iustin Pop
    value += 4 - value % 4
693 a8083063 Iustin Pop
694 a8083063 Iustin Pop
  return value
695 a8083063 Iustin Pop
696 a8083063 Iustin Pop
697 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
698 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
699 a8083063 Iustin Pop

700 a8083063 Iustin Pop
  Args:
701 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
702 a8083063 Iustin Pop
    key: String containing key
703 a8083063 Iustin Pop
  """
704 a8083063 Iustin Pop
  key_fields = key.split()
705 a8083063 Iustin Pop
706 a8083063 Iustin Pop
  f = open(file_name, 'a+')
707 a8083063 Iustin Pop
  try:
708 a8083063 Iustin Pop
    nl = True
709 a8083063 Iustin Pop
    for line in f:
710 a8083063 Iustin Pop
      # Ignore whitespace changes
711 a8083063 Iustin Pop
      if line.split() == key_fields:
712 a8083063 Iustin Pop
        break
713 a8083063 Iustin Pop
      nl = line.endswith('\n')
714 a8083063 Iustin Pop
    else:
715 a8083063 Iustin Pop
      if not nl:
716 a8083063 Iustin Pop
        f.write("\n")
717 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
718 a8083063 Iustin Pop
      f.write("\n")
719 a8083063 Iustin Pop
      f.flush()
720 a8083063 Iustin Pop
  finally:
721 a8083063 Iustin Pop
    f.close()
722 a8083063 Iustin Pop
723 a8083063 Iustin Pop
724 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
725 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
726 a8083063 Iustin Pop

727 a8083063 Iustin Pop
  Args:
728 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
729 a8083063 Iustin Pop
    key: String containing key
730 a8083063 Iustin Pop
  """
731 a8083063 Iustin Pop
  key_fields = key.split()
732 a8083063 Iustin Pop
733 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
734 a8083063 Iustin Pop
  try:
735 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
736 a8083063 Iustin Pop
    try:
737 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
738 59f82e3f Michael Hanselmann
      try:
739 59f82e3f Michael Hanselmann
        for line in f:
740 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
741 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
742 59f82e3f Michael Hanselmann
            out.write(line)
743 899d2a81 Michael Hanselmann
744 899d2a81 Michael Hanselmann
        out.flush()
745 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
746 899d2a81 Michael Hanselmann
      finally:
747 899d2a81 Michael Hanselmann
        f.close()
748 899d2a81 Michael Hanselmann
    finally:
749 899d2a81 Michael Hanselmann
      out.close()
750 899d2a81 Michael Hanselmann
  except:
751 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
752 899d2a81 Michael Hanselmann
    raise
753 899d2a81 Michael Hanselmann
754 899d2a81 Michael Hanselmann
755 899d2a81 Michael Hanselmann
def AddEtcHostsEntry(file_name, hostname, ip):
756 899d2a81 Michael Hanselmann
  """
757 899d2a81 Michael Hanselmann

758 899d2a81 Michael Hanselmann
  """
759 899d2a81 Michael Hanselmann
  f = open(file_name, 'a+')
760 899d2a81 Michael Hanselmann
  try:
761 899d2a81 Michael Hanselmann
    nl = True
762 899d2a81 Michael Hanselmann
    for line in f:
763 899d2a81 Michael Hanselmann
      fields = line.split()
764 899d2a81 Michael Hanselmann
      if len(fields) < 2 or fields[0].startswith('#'):
765 899d2a81 Michael Hanselmann
        continue
766 899d2a81 Michael Hanselmann
      if fields[0] == ip and hostname in fields[1:]:
767 899d2a81 Michael Hanselmann
        break
768 899d2a81 Michael Hanselmann
      nl = line.endswith('\n')
769 899d2a81 Michael Hanselmann
    else:
770 899d2a81 Michael Hanselmann
      if not nl:
771 899d2a81 Michael Hanselmann
        f.write("\n")
772 899d2a81 Michael Hanselmann
      f.write(ip)
773 899d2a81 Michael Hanselmann
      f.write(' ')
774 899d2a81 Michael Hanselmann
      f.write(hostname)
775 899d2a81 Michael Hanselmann
      f.write("\n")
776 899d2a81 Michael Hanselmann
      f.flush()
777 899d2a81 Michael Hanselmann
  finally:
778 899d2a81 Michael Hanselmann
    f.close()
779 899d2a81 Michael Hanselmann
780 899d2a81 Michael Hanselmann
781 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
782 899d2a81 Michael Hanselmann
  """
783 899d2a81 Michael Hanselmann

784 899d2a81 Michael Hanselmann
  """
785 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
786 899d2a81 Michael Hanselmann
  try:
787 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
788 899d2a81 Michael Hanselmann
    try:
789 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
790 899d2a81 Michael Hanselmann
      try:
791 899d2a81 Michael Hanselmann
        for line in f:
792 899d2a81 Michael Hanselmann
          fields = line.split()
793 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
794 899d2a81 Michael Hanselmann
            names = fields[1:]
795 899d2a81 Michael Hanselmann
            if hostname in names:
796 899d2a81 Michael Hanselmann
              while hostname in names:
797 899d2a81 Michael Hanselmann
                names.remove(hostname)
798 899d2a81 Michael Hanselmann
              if names:
799 899d2a81 Michael Hanselmann
                out.write(fields[0])
800 899d2a81 Michael Hanselmann
                out.write(' ')
801 899d2a81 Michael Hanselmann
                out.write(' '.join(names))
802 899d2a81 Michael Hanselmann
              continue
803 899d2a81 Michael Hanselmann
804 899d2a81 Michael Hanselmann
          out.write(line)
805 59f82e3f Michael Hanselmann
806 59f82e3f Michael Hanselmann
        out.flush()
807 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
808 59f82e3f Michael Hanselmann
      finally:
809 59f82e3f Michael Hanselmann
        f.close()
810 a8083063 Iustin Pop
    finally:
811 59f82e3f Michael Hanselmann
      out.close()
812 59f82e3f Michael Hanselmann
  except:
813 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
814 59f82e3f Michael Hanselmann
    raise
815 a8083063 Iustin Pop
816 a8083063 Iustin Pop
817 a8083063 Iustin Pop
def CreateBackup(file_name):
818 a8083063 Iustin Pop
  """Creates a backup of a file.
819 a8083063 Iustin Pop

820 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
821 a8083063 Iustin Pop

822 a8083063 Iustin Pop
  """
823 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
824 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
825 3ecf6786 Iustin Pop
                                file_name)
826 a8083063 Iustin Pop
827 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
828 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
829 081b1e69 Michael Hanselmann
830 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
831 081b1e69 Michael Hanselmann
  try:
832 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
833 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
834 081b1e69 Michael Hanselmann
    try:
835 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
836 081b1e69 Michael Hanselmann
    finally:
837 081b1e69 Michael Hanselmann
      fdst.close()
838 081b1e69 Michael Hanselmann
  finally:
839 081b1e69 Michael Hanselmann
    fsrc.close()
840 081b1e69 Michael Hanselmann
841 a8083063 Iustin Pop
  return backup_name
842 a8083063 Iustin Pop
843 a8083063 Iustin Pop
844 a8083063 Iustin Pop
def ShellQuote(value):
845 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
846 3ecf6786 Iustin Pop

847 a8083063 Iustin Pop
  """
848 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
849 a8083063 Iustin Pop
    return value
850 a8083063 Iustin Pop
  else:
851 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
852 a8083063 Iustin Pop
853 a8083063 Iustin Pop
854 a8083063 Iustin Pop
def ShellQuoteArgs(args):
855 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
856 a8083063 Iustin Pop

857 a8083063 Iustin Pop
  """
858 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
859 88d14415 Michael Hanselmann
860 88d14415 Michael Hanselmann
861 2c30e9d7 Alexander Schreiber
862 16abfbc2 Alexander Schreiber
def TcpPing(source, target, port, timeout=10, live_port_needed=False):
863 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
864 2c30e9d7 Alexander Schreiber

865 2c30e9d7 Alexander Schreiber
  Try to do a TCP connect(2) from the specified source IP to the specified
866 2c30e9d7 Alexander Schreiber
  target IP and the specified target port. If live_port_needed is set to true,
867 2c30e9d7 Alexander Schreiber
  requires the remote end to accept the connection. The timeout is specified
868 2c30e9d7 Alexander Schreiber
  in seconds and defaults to 10 seconds
869 2c30e9d7 Alexander Schreiber

870 2c30e9d7 Alexander Schreiber
  """
871 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
872 2c30e9d7 Alexander Schreiber
873 2c30e9d7 Alexander Schreiber
  sucess = False
874 2c30e9d7 Alexander Schreiber
875 2c30e9d7 Alexander Schreiber
  try:
876 2c30e9d7 Alexander Schreiber
    sock.bind((source, 0))
877 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
878 4ca1b175 Alexander Schreiber
    if errcode == errno.EADDRNOTAVAIL:
879 2c30e9d7 Alexander Schreiber
      success = False
880 2c30e9d7 Alexander Schreiber
881 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
882 2c30e9d7 Alexander Schreiber
883 2c30e9d7 Alexander Schreiber
  try:
884 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
885 2c30e9d7 Alexander Schreiber
    sock.close()
886 2c30e9d7 Alexander Schreiber
    success = True
887 2c30e9d7 Alexander Schreiber
  except socket.timeout:
888 2c30e9d7 Alexander Schreiber
    success = False
889 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
890 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
891 2c30e9d7 Alexander Schreiber
892 2c30e9d7 Alexander Schreiber
  return success
893 eedbda4b Michael Hanselmann
894 eedbda4b Michael Hanselmann
895 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
896 eedbda4b Michael Hanselmann
  """Returns a list of all visible files in a directory.
897 eedbda4b Michael Hanselmann

898 eedbda4b Michael Hanselmann
  """
899 eedbda4b Michael Hanselmann
  return [i for i in os.listdir(path) if not i.startswith(".")]
900 2f8b60b3 Iustin Pop
901 2f8b60b3 Iustin Pop
902 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
903 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
904 257f4c0a Iustin Pop

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

909 2f8b60b3 Iustin Pop
  """
910 2f8b60b3 Iustin Pop
  try:
911 257f4c0a Iustin Pop
    if isinstance(user, basestring):
912 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
913 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
914 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
915 257f4c0a Iustin Pop
    else:
916 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
917 257f4c0a Iustin Pop
                                   type(user))
918 2f8b60b3 Iustin Pop
  except KeyError:
919 2f8b60b3 Iustin Pop
    return default
920 2f8b60b3 Iustin Pop
  return result.pw_dir
921 59072e7e Michael Hanselmann
922 59072e7e Michael Hanselmann
923 24818e8f Michael Hanselmann
def NewUUID():
924 59072e7e Michael Hanselmann
  """Returns a random UUID.
925 59072e7e Michael Hanselmann

926 59072e7e Michael Hanselmann
  """
927 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
928 59072e7e Michael Hanselmann
  try:
929 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
930 59072e7e Michael Hanselmann
  finally:
931 59072e7e Michael Hanselmann
    f.close()
932 087b34fe Iustin Pop
933 087b34fe Iustin Pop
934 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
935 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
936 087b34fe Iustin Pop
              atime=None, mtime=None):
937 087b34fe Iustin Pop
  """(Over)write a file atomically.
938 087b34fe Iustin Pop

939 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
940 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
941 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
942 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
943 087b34fe Iustin Pop
  mtime/atime of the file.
944 087b34fe Iustin Pop

945 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
946 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
947 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
948 087b34fe Iustin Pop
  temporary file should be removed.
949 087b34fe Iustin Pop

950 087b34fe Iustin Pop
  """
951 087b34fe Iustin Pop
  if not os.path.isabs(file_name):
952 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
953 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
954 087b34fe Iustin Pop
955 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
956 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
957 087b34fe Iustin Pop
958 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
959 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
960 087b34fe Iustin Pop
                                 " set or None")
961 087b34fe Iustin Pop
962 087b34fe Iustin Pop
963 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
964 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
965 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
966 087b34fe Iustin Pop
  # leaves it in place
967 087b34fe Iustin Pop
  try:
968 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
969 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
970 087b34fe Iustin Pop
    if mode:
971 087b34fe Iustin Pop
      os.chmod(new_name, mode)
972 087b34fe Iustin Pop
    if data is not None:
973 087b34fe Iustin Pop
      os.write(fd, data)
974 087b34fe Iustin Pop
    else:
975 087b34fe Iustin Pop
      fn(fd)
976 087b34fe Iustin Pop
    os.fsync(fd)
977 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
978 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
979 087b34fe Iustin Pop
    os.rename(new_name, file_name)
980 087b34fe Iustin Pop
  finally:
981 087b34fe Iustin Pop
    os.close(fd)
982 087b34fe Iustin Pop
    RemoveFile(new_name)
983 78feb6fb Guido Trotter
984 78feb6fb Guido Trotter
985 78feb6fb Guido Trotter
def all(seq, pred=bool):
986 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
987 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
988 78feb6fb Guido Trotter
    return False
989 78feb6fb Guido Trotter
  return True
990 78feb6fb Guido Trotter
991 78feb6fb Guido Trotter
992 78feb6fb Guido Trotter
def any(seq, pred=bool):
993 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
994 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
995 78feb6fb Guido Trotter
    return True
996 78feb6fb Guido Trotter
  return False