Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 7996a135

History | View | Annotate | Download (27.3 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 9c233417 Iustin Pop
import select
40 9c233417 Iustin Pop
import fcntl
41 8f765069 Iustin Pop
import resource
42 bb698c1f Iustin Pop
import logging
43 9c233417 Iustin Pop
44 9c233417 Iustin Pop
from cStringIO import StringIO
45 a8083063 Iustin Pop
46 a8083063 Iustin Pop
from ganeti import errors
47 3aecd2c7 Iustin Pop
from ganeti import constants
48 a8083063 Iustin Pop
49 16abfbc2 Alexander Schreiber
50 a8083063 Iustin Pop
_locksheld = []
51 a8083063 Iustin Pop
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
52 a8083063 Iustin Pop
53 f362096f Iustin Pop
debug = False
54 b74159ee Iustin Pop
no_fork = False
55 f362096f Iustin Pop
56 7c0d6283 Michael Hanselmann
57 a8083063 Iustin Pop
class RunResult(object):
58 a8083063 Iustin Pop
  """Simple class for holding the result of running external programs.
59 a8083063 Iustin Pop

60 a8083063 Iustin Pop
  Instance variables:
61 a8083063 Iustin Pop
    exit_code: the exit code of the program, or None (if the program
62 a8083063 Iustin Pop
               didn't exit())
63 a8083063 Iustin Pop
    signal: numeric signal that caused the program to finish, or None
64 a8083063 Iustin Pop
            (if the program wasn't terminated by a signal)
65 a8083063 Iustin Pop
    stdout: the standard output of the program
66 a8083063 Iustin Pop
    stderr: the standard error of the program
67 a8083063 Iustin Pop
    failed: a Boolean value which is True in case the program was
68 a8083063 Iustin Pop
            terminated by a signal or exited with a non-zero exit code
69 a8083063 Iustin Pop
    fail_reason: a string detailing the termination reason
70 a8083063 Iustin Pop

71 a8083063 Iustin Pop
  """
72 a8083063 Iustin Pop
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
73 a8083063 Iustin Pop
               "failed", "fail_reason", "cmd"]
74 a8083063 Iustin Pop
75 a8083063 Iustin Pop
76 a8083063 Iustin Pop
  def __init__(self, exit_code, signal, stdout, stderr, cmd):
77 a8083063 Iustin Pop
    self.cmd = cmd
78 a8083063 Iustin Pop
    self.exit_code = exit_code
79 a8083063 Iustin Pop
    self.signal = signal
80 a8083063 Iustin Pop
    self.stdout = stdout
81 a8083063 Iustin Pop
    self.stderr = stderr
82 a8083063 Iustin Pop
    self.failed = (signal is not None or exit_code != 0)
83 a8083063 Iustin Pop
84 a8083063 Iustin Pop
    if self.signal is not None:
85 a8083063 Iustin Pop
      self.fail_reason = "terminated by signal %s" % self.signal
86 a8083063 Iustin Pop
    elif self.exit_code is not None:
87 a8083063 Iustin Pop
      self.fail_reason = "exited with exit code %s" % self.exit_code
88 a8083063 Iustin Pop
    else:
89 a8083063 Iustin Pop
      self.fail_reason = "unable to determine termination reason"
90 a8083063 Iustin Pop
91 bb698c1f Iustin Pop
    if self.failed:
92 bb698c1f Iustin Pop
      logging.debug("Command '%s' failed (%s); output: %s",
93 bb698c1f Iustin Pop
                    self.cmd, self.fail_reason, self.output)
94 f362096f Iustin Pop
95 a8083063 Iustin Pop
  def _GetOutput(self):
96 a8083063 Iustin Pop
    """Returns the combined stdout and stderr for easier usage.
97 a8083063 Iustin Pop

98 a8083063 Iustin Pop
    """
99 a8083063 Iustin Pop
    return self.stdout + self.stderr
100 a8083063 Iustin Pop
101 a8083063 Iustin Pop
  output = property(_GetOutput, None, None, "Return full output")
102 a8083063 Iustin Pop
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
def RunCmd(cmd):
105 a8083063 Iustin Pop
  """Execute a (shell) command.
106 a8083063 Iustin Pop

107 a8083063 Iustin Pop
  The command should not read from its standard input, as it will be
108 a8083063 Iustin Pop
  closed.
109 a8083063 Iustin Pop

110 a8083063 Iustin Pop
  Args:
111 a8083063 Iustin Pop
    cmd: command to run. (str)
112 a8083063 Iustin Pop

113 a8083063 Iustin Pop
  Returns: `RunResult` instance
114 a8083063 Iustin Pop

115 a8083063 Iustin Pop
  """
116 b74159ee Iustin Pop
  if no_fork:
117 b74159ee Iustin Pop
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
118 b74159ee Iustin Pop
119 a8083063 Iustin Pop
  if isinstance(cmd, list):
120 a8083063 Iustin Pop
    cmd = [str(val) for val in cmd]
121 113b55aa Iustin Pop
    strcmd = " ".join(cmd)
122 113b55aa Iustin Pop
    shell = False
123 113b55aa Iustin Pop
  else:
124 113b55aa Iustin Pop
    strcmd = cmd
125 113b55aa Iustin Pop
    shell = True
126 bb698c1f Iustin Pop
  logging.debug("RunCmd '%s'", strcmd)
127 23f41a3e Michael Hanselmann
  env = os.environ.copy()
128 23f41a3e Michael Hanselmann
  env["LC_ALL"] = "C"
129 9c233417 Iustin Pop
  poller = select.poll()
130 113b55aa Iustin Pop
  child = subprocess.Popen(cmd, shell=shell,
131 113b55aa Iustin Pop
                           stderr=subprocess.PIPE,
132 113b55aa Iustin Pop
                           stdout=subprocess.PIPE,
133 113b55aa Iustin Pop
                           stdin=subprocess.PIPE,
134 23f41a3e Michael Hanselmann
                           close_fds=True, env=env)
135 113b55aa Iustin Pop
136 113b55aa Iustin Pop
  child.stdin.close()
137 9c233417 Iustin Pop
  poller.register(child.stdout, select.POLLIN)
138 9c233417 Iustin Pop
  poller.register(child.stderr, select.POLLIN)
139 9c233417 Iustin Pop
  out = StringIO()
140 9c233417 Iustin Pop
  err = StringIO()
141 9c233417 Iustin Pop
  fdmap = {
142 9c233417 Iustin Pop
    child.stdout.fileno(): (out, child.stdout),
143 9c233417 Iustin Pop
    child.stderr.fileno(): (err, child.stderr),
144 9c233417 Iustin Pop
    }
145 9c233417 Iustin Pop
  for fd in fdmap:
146 9c233417 Iustin Pop
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
147 9c233417 Iustin Pop
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
148 9c233417 Iustin Pop
149 9c233417 Iustin Pop
  while fdmap:
150 9c233417 Iustin Pop
    for fd, event in poller.poll():
151 9c233417 Iustin Pop
      if event & select.POLLIN or event & select.POLLPRI:
152 9c233417 Iustin Pop
        data = fdmap[fd][1].read()
153 9c233417 Iustin Pop
        # no data from read signifies EOF (the same as POLLHUP)
154 9c233417 Iustin Pop
        if not data:
155 9c233417 Iustin Pop
          poller.unregister(fd)
156 9c233417 Iustin Pop
          del fdmap[fd]
157 9c233417 Iustin Pop
          continue
158 9c233417 Iustin Pop
        fdmap[fd][0].write(data)
159 9c233417 Iustin Pop
      if (event & select.POLLNVAL or event & select.POLLHUP or
160 9c233417 Iustin Pop
          event & select.POLLERR):
161 9c233417 Iustin Pop
        poller.unregister(fd)
162 9c233417 Iustin Pop
        del fdmap[fd]
163 9c233417 Iustin Pop
164 9c233417 Iustin Pop
  out = out.getvalue()
165 9c233417 Iustin Pop
  err = err.getvalue()
166 a8083063 Iustin Pop
167 a8083063 Iustin Pop
  status = child.wait()
168 113b55aa Iustin Pop
  if status >= 0:
169 113b55aa Iustin Pop
    exitcode = status
170 a8083063 Iustin Pop
    signal = None
171 a8083063 Iustin Pop
  else:
172 a8083063 Iustin Pop
    exitcode = None
173 113b55aa Iustin Pop
    signal = -status
174 a8083063 Iustin Pop
175 a8083063 Iustin Pop
  return RunResult(exitcode, signal, out, err, strcmd)
176 a8083063 Iustin Pop
177 a8083063 Iustin Pop
178 a8083063 Iustin Pop
def RemoveFile(filename):
179 a8083063 Iustin Pop
  """Remove a file ignoring some errors.
180 a8083063 Iustin Pop

181 a8083063 Iustin Pop
  Remove a file, ignoring non-existing ones or directories. Other
182 a8083063 Iustin Pop
  errors are passed.
183 a8083063 Iustin Pop

184 a8083063 Iustin Pop
  """
185 a8083063 Iustin Pop
  try:
186 a8083063 Iustin Pop
    os.unlink(filename)
187 a8083063 Iustin Pop
  except OSError, err:
188 4ca1b175 Alexander Schreiber
    if err.errno not in (errno.ENOENT, errno.EISDIR):
189 a8083063 Iustin Pop
      raise
190 a8083063 Iustin Pop
191 a8083063 Iustin Pop
192 a8083063 Iustin Pop
def _FingerprintFile(filename):
193 a8083063 Iustin Pop
  """Compute the fingerprint of a file.
194 a8083063 Iustin Pop

195 a8083063 Iustin Pop
  If the file does not exist, a None will be returned
196 a8083063 Iustin Pop
  instead.
197 a8083063 Iustin Pop

198 a8083063 Iustin Pop
  Args:
199 a8083063 Iustin Pop
    filename - Filename (str)
200 a8083063 Iustin Pop

201 a8083063 Iustin Pop
  """
202 a8083063 Iustin Pop
  if not (os.path.exists(filename) and os.path.isfile(filename)):
203 a8083063 Iustin Pop
    return None
204 a8083063 Iustin Pop
205 a8083063 Iustin Pop
  f = open(filename)
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
  fp = sha.sha()
208 a8083063 Iustin Pop
  while True:
209 a8083063 Iustin Pop
    data = f.read(4096)
210 a8083063 Iustin Pop
    if not data:
211 a8083063 Iustin Pop
      break
212 a8083063 Iustin Pop
213 a8083063 Iustin Pop
    fp.update(data)
214 a8083063 Iustin Pop
215 a8083063 Iustin Pop
  return fp.hexdigest()
216 a8083063 Iustin Pop
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
def FingerprintFiles(files):
219 a8083063 Iustin Pop
  """Compute fingerprints for a list of files.
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
  Args:
222 a8083063 Iustin Pop
    files - array of filenames.  ( [str, ...] )
223 a8083063 Iustin Pop

224 a8083063 Iustin Pop
  Return value:
225 a8083063 Iustin Pop
    dictionary of filename: fingerprint for the files that exist
226 a8083063 Iustin Pop

227 a8083063 Iustin Pop
  """
228 a8083063 Iustin Pop
  ret = {}
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
  for filename in files:
231 a8083063 Iustin Pop
    cksum = _FingerprintFile(filename)
232 a8083063 Iustin Pop
    if cksum:
233 a8083063 Iustin Pop
      ret[filename] = cksum
234 a8083063 Iustin Pop
235 a8083063 Iustin Pop
  return ret
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
def CheckDict(target, template, logname=None):
239 a8083063 Iustin Pop
  """Ensure a dictionary has a required set of keys.
240 a8083063 Iustin Pop

241 a8083063 Iustin Pop
  For the given dictionaries `target` and `template`, ensure target
242 a8083063 Iustin Pop
  has all the keys from template. Missing keys are added with values
243 a8083063 Iustin Pop
  from template.
244 a8083063 Iustin Pop

245 a8083063 Iustin Pop
  Args:
246 a8083063 Iustin Pop
    target   - the dictionary to check
247 a8083063 Iustin Pop
    template - template dictionary
248 a8083063 Iustin Pop
    logname  - a caller-chosen string to identify the debug log
249 a8083063 Iustin Pop
               entry; if None, no logging will be done
250 a8083063 Iustin Pop

251 a8083063 Iustin Pop
  Returns value:
252 a8083063 Iustin Pop
    None
253 a8083063 Iustin Pop

254 a8083063 Iustin Pop
  """
255 a8083063 Iustin Pop
  missing = []
256 a8083063 Iustin Pop
  for k in template:
257 a8083063 Iustin Pop
    if k not in target:
258 a8083063 Iustin Pop
      missing.append(k)
259 a8083063 Iustin Pop
      target[k] = template[k]
260 a8083063 Iustin Pop
261 a8083063 Iustin Pop
  if missing and logname:
262 bb698c1f Iustin Pop
    logging.warning('%s missing keys %s', logname, ', '.join(missing))
263 a8083063 Iustin Pop
264 a8083063 Iustin Pop
265 a8083063 Iustin Pop
def IsProcessAlive(pid):
266 a8083063 Iustin Pop
  """Check if a given pid exists on the system.
267 a8083063 Iustin Pop

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

270 a8083063 Iustin Pop
  Remarks: zombie processes treated as not alive
271 a8083063 Iustin Pop

272 a8083063 Iustin Pop
  """
273 a8083063 Iustin Pop
  try:
274 a8083063 Iustin Pop
    f = open("/proc/%d/status" % pid)
275 a8083063 Iustin Pop
  except IOError, err:
276 4ca1b175 Alexander Schreiber
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
277 a8083063 Iustin Pop
      return False
278 a8083063 Iustin Pop
279 a8083063 Iustin Pop
  alive = True
280 a8083063 Iustin Pop
  try:
281 a8083063 Iustin Pop
    data = f.readlines()
282 a8083063 Iustin Pop
    if len(data) > 1:
283 a8083063 Iustin Pop
      state = data[1].split()
284 a8083063 Iustin Pop
      if len(state) > 1 and state[1] == "Z":
285 a8083063 Iustin Pop
        alive = False
286 a8083063 Iustin Pop
  finally:
287 a8083063 Iustin Pop
    f.close()
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
  return alive
290 a8083063 Iustin Pop
291 a8083063 Iustin Pop
292 a8083063 Iustin Pop
def MatchNameComponent(key, name_list):
293 a8083063 Iustin Pop
  """Try to match a name against a list.
294 a8083063 Iustin Pop

295 a8083063 Iustin Pop
  This function will try to match a name like test1 against a list
296 a8083063 Iustin Pop
  like ['test1.example.com', 'test2.example.com', ...]. Against this
297 a8083063 Iustin Pop
  list, 'test1' as well as 'test1.example' will match, but not
298 a8083063 Iustin Pop
  'test1.ex'. A multiple match will be considered as no match at all
299 a8083063 Iustin Pop
  (e.g. 'test1' against ['test1.example.com', 'test1.example.org']).
300 a8083063 Iustin Pop

301 a8083063 Iustin Pop
  Args:
302 a8083063 Iustin Pop
    key: the name to be searched
303 a8083063 Iustin Pop
    name_list: the list of strings against which to search the key
304 a8083063 Iustin Pop

305 a8083063 Iustin Pop
  Returns:
306 a8083063 Iustin Pop
    None if there is no match *or* if there are multiple matches
307 a8083063 Iustin Pop
    otherwise the element from the list which matches
308 a8083063 Iustin Pop

309 a8083063 Iustin Pop
  """
310 a8083063 Iustin Pop
  mo = re.compile("^%s(\..*)?$" % re.escape(key))
311 a8083063 Iustin Pop
  names_filtered = [name for name in name_list if mo.match(name) is not None]
312 a8083063 Iustin Pop
  if len(names_filtered) != 1:
313 a8083063 Iustin Pop
    return None
314 a8083063 Iustin Pop
  return names_filtered[0]
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
317 bcf043c9 Iustin Pop
class HostInfo:
318 89e1fc26 Iustin Pop
  """Class implementing resolver and hostname functionality
319 bcf043c9 Iustin Pop

320 bcf043c9 Iustin Pop
  """
321 89e1fc26 Iustin Pop
  def __init__(self, name=None):
322 bcf043c9 Iustin Pop
    """Initialize the host name object.
323 bcf043c9 Iustin Pop

324 89e1fc26 Iustin Pop
    If the name argument is not passed, it will use this system's
325 89e1fc26 Iustin Pop
    name.
326 bcf043c9 Iustin Pop

327 bcf043c9 Iustin Pop
    """
328 89e1fc26 Iustin Pop
    if name is None:
329 89e1fc26 Iustin Pop
      name = self.SysName()
330 89e1fc26 Iustin Pop
331 89e1fc26 Iustin Pop
    self.query = name
332 89e1fc26 Iustin Pop
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
333 bcf043c9 Iustin Pop
    self.ip = self.ipaddrs[0]
334 bcf043c9 Iustin Pop
335 c8a0948f Michael Hanselmann
  def ShortName(self):
336 c8a0948f Michael Hanselmann
    """Returns the hostname without domain.
337 c8a0948f Michael Hanselmann

338 c8a0948f Michael Hanselmann
    """
339 c8a0948f Michael Hanselmann
    return self.name.split('.')[0]
340 c8a0948f Michael Hanselmann
341 89e1fc26 Iustin Pop
  @staticmethod
342 89e1fc26 Iustin Pop
  def SysName():
343 89e1fc26 Iustin Pop
    """Return the current system's name.
344 bcf043c9 Iustin Pop

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

347 89e1fc26 Iustin Pop
    """
348 89e1fc26 Iustin Pop
    return socket.gethostname()
349 a8083063 Iustin Pop
350 89e1fc26 Iustin Pop
  @staticmethod
351 89e1fc26 Iustin Pop
  def LookupHostname(hostname):
352 89e1fc26 Iustin Pop
    """Look up hostname
353 a8083063 Iustin Pop

354 89e1fc26 Iustin Pop
    Args:
355 89e1fc26 Iustin Pop
      hostname: hostname to look up
356 89e1fc26 Iustin Pop

357 89e1fc26 Iustin Pop
    Returns:
358 89e1fc26 Iustin Pop
      a tuple (name, aliases, ipaddrs) as returned by socket.gethostbyname_ex
359 89e1fc26 Iustin Pop
      in case of errors in resolving, we raise a ResolverError
360 89e1fc26 Iustin Pop

361 89e1fc26 Iustin Pop
    """
362 89e1fc26 Iustin Pop
    try:
363 89e1fc26 Iustin Pop
      result = socket.gethostbyname_ex(hostname)
364 89e1fc26 Iustin Pop
    except socket.gaierror, err:
365 89e1fc26 Iustin Pop
      # hostname not found in DNS
366 89e1fc26 Iustin Pop
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
367 a8083063 Iustin Pop
368 89e1fc26 Iustin Pop
    return result
369 a8083063 Iustin Pop
370 a8083063 Iustin Pop
371 a8083063 Iustin Pop
def ListVolumeGroups():
372 a8083063 Iustin Pop
  """List volume groups and their size
373 a8083063 Iustin Pop

374 a8083063 Iustin Pop
  Returns:
375 a8083063 Iustin Pop
     Dictionary with keys volume name and values the size of the volume
376 a8083063 Iustin Pop

377 a8083063 Iustin Pop
  """
378 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
379 a8083063 Iustin Pop
  result = RunCmd(command)
380 a8083063 Iustin Pop
  retval = {}
381 a8083063 Iustin Pop
  if result.failed:
382 a8083063 Iustin Pop
    return retval
383 a8083063 Iustin Pop
384 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
385 a8083063 Iustin Pop
    try:
386 a8083063 Iustin Pop
      name, size = line.split()
387 a8083063 Iustin Pop
      size = int(float(size))
388 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
389 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
390 a8083063 Iustin Pop
      continue
391 a8083063 Iustin Pop
392 a8083063 Iustin Pop
    retval[name] = size
393 a8083063 Iustin Pop
394 a8083063 Iustin Pop
  return retval
395 a8083063 Iustin Pop
396 a8083063 Iustin Pop
397 a8083063 Iustin Pop
def BridgeExists(bridge):
398 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
399 a8083063 Iustin Pop

400 a8083063 Iustin Pop
  Returns:
401 a8083063 Iustin Pop
     True if it does, false otherwise.
402 a8083063 Iustin Pop

403 a8083063 Iustin Pop
  """
404 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
405 a8083063 Iustin Pop
406 a8083063 Iustin Pop
407 a8083063 Iustin Pop
def NiceSort(name_list):
408 a8083063 Iustin Pop
  """Sort a list of strings based on digit and non-digit groupings.
409 a8083063 Iustin Pop

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

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

417 a8083063 Iustin Pop
  Return value
418 a8083063 Iustin Pop
    - a copy of the list sorted according to our algorithm
419 a8083063 Iustin Pop

420 a8083063 Iustin Pop
  """
421 a8083063 Iustin Pop
  _SORTER_BASE = "(\D+|\d+)"
422 a8083063 Iustin Pop
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
423 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
424 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE,
425 a8083063 Iustin Pop
                                                  _SORTER_BASE, _SORTER_BASE)
426 a8083063 Iustin Pop
  _SORTER_RE = re.compile(_SORTER_FULL)
427 a8083063 Iustin Pop
  _SORTER_NODIGIT = re.compile("^\D*$")
428 a8083063 Iustin Pop
  def _TryInt(val):
429 a8083063 Iustin Pop
    """Attempts to convert a variable to integer."""
430 a8083063 Iustin Pop
    if val is None or _SORTER_NODIGIT.match(val):
431 a8083063 Iustin Pop
      return val
432 a8083063 Iustin Pop
    rval = int(val)
433 a8083063 Iustin Pop
    return rval
434 a8083063 Iustin Pop
435 a8083063 Iustin Pop
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
436 a8083063 Iustin Pop
             for name in name_list]
437 a8083063 Iustin Pop
  to_sort.sort()
438 a8083063 Iustin Pop
  return [tup[1] for tup in to_sort]
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
441 a8083063 Iustin Pop
def TryConvert(fn, val):
442 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
443 a8083063 Iustin Pop

444 a8083063 Iustin Pop
  This function tries to apply function `fn` to `val`. If no
445 a8083063 Iustin Pop
  ValueError or TypeError exceptions are raised, it will return the
446 a8083063 Iustin Pop
  result, else it will return the original value. Any other exceptions
447 a8083063 Iustin Pop
  are propagated to the caller.
448 a8083063 Iustin Pop

449 a8083063 Iustin Pop
  """
450 a8083063 Iustin Pop
  try:
451 a8083063 Iustin Pop
    nv = fn(val)
452 a8083063 Iustin Pop
  except (ValueError, TypeError), err:
453 a8083063 Iustin Pop
    nv = val
454 a8083063 Iustin Pop
  return nv
455 a8083063 Iustin Pop
456 a8083063 Iustin Pop
457 a8083063 Iustin Pop
def IsValidIP(ip):
458 a8083063 Iustin Pop
  """Verifies the syntax of an IP address.
459 a8083063 Iustin Pop

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

463 a8083063 Iustin Pop
  """
464 a8083063 Iustin Pop
  unit = "(0|[1-9]\d{0,2})"
465 a8083063 Iustin Pop
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
468 a8083063 Iustin Pop
def IsValidShellParam(word):
469 a8083063 Iustin Pop
  """Verifies is the given word is safe from the shell's p.o.v.
470 a8083063 Iustin Pop

471 a8083063 Iustin Pop
  This means that we can pass this to a command via the shell and be
472 a8083063 Iustin Pop
  sure that it doesn't alter the command line and is passed as such to
473 a8083063 Iustin Pop
  the actual command.
474 a8083063 Iustin Pop

475 a8083063 Iustin Pop
  Note that we are overly restrictive here, in order to be on the safe
476 a8083063 Iustin Pop
  side.
477 a8083063 Iustin Pop

478 a8083063 Iustin Pop
  """
479 a8083063 Iustin Pop
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
480 a8083063 Iustin Pop
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
def BuildShellCmd(template, *args):
483 a8083063 Iustin Pop
  """Build a safe shell command line from the given arguments.
484 a8083063 Iustin Pop

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

490 a8083063 Iustin Pop
  """
491 a8083063 Iustin Pop
  for word in args:
492 a8083063 Iustin Pop
    if not IsValidShellParam(word):
493 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Shell argument '%s' contains"
494 3ecf6786 Iustin Pop
                                   " invalid characters" % word)
495 a8083063 Iustin Pop
  return template % args
496 a8083063 Iustin Pop
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
def FormatUnit(value):
499 a8083063 Iustin Pop
  """Formats an incoming number of MiB with the appropriate unit.
500 a8083063 Iustin Pop

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

503 a8083063 Iustin Pop
  """
504 a8083063 Iustin Pop
  if value < 1024:
505 a8083063 Iustin Pop
    return "%dM" % round(value, 0)
506 a8083063 Iustin Pop
507 a8083063 Iustin Pop
  elif value < (1024 * 1024):
508 a8083063 Iustin Pop
    return "%0.1fG" % round(float(value) / 1024, 1)
509 a8083063 Iustin Pop
510 a8083063 Iustin Pop
  else:
511 a8083063 Iustin Pop
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
512 a8083063 Iustin Pop
513 a8083063 Iustin Pop
514 a8083063 Iustin Pop
def ParseUnit(input_string):
515 a8083063 Iustin Pop
  """Tries to extract number and scale from the given string.
516 a8083063 Iustin Pop

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

520 a8083063 Iustin Pop
  """
521 a8083063 Iustin Pop
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', input_string)
522 a8083063 Iustin Pop
  if not m:
523 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Invalid format")
524 a8083063 Iustin Pop
525 a8083063 Iustin Pop
  value = float(m.groups()[0])
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
  unit = m.groups()[1]
528 a8083063 Iustin Pop
  if unit:
529 a8083063 Iustin Pop
    lcunit = unit.lower()
530 a8083063 Iustin Pop
  else:
531 a8083063 Iustin Pop
    lcunit = 'm'
532 a8083063 Iustin Pop
533 a8083063 Iustin Pop
  if lcunit in ('m', 'mb', 'mib'):
534 a8083063 Iustin Pop
    # Value already in MiB
535 a8083063 Iustin Pop
    pass
536 a8083063 Iustin Pop
537 a8083063 Iustin Pop
  elif lcunit in ('g', 'gb', 'gib'):
538 a8083063 Iustin Pop
    value *= 1024
539 a8083063 Iustin Pop
540 a8083063 Iustin Pop
  elif lcunit in ('t', 'tb', 'tib'):
541 a8083063 Iustin Pop
    value *= 1024 * 1024
542 a8083063 Iustin Pop
543 a8083063 Iustin Pop
  else:
544 3ecf6786 Iustin Pop
    raise errors.UnitParseError("Unknown unit: %s" % unit)
545 a8083063 Iustin Pop
546 a8083063 Iustin Pop
  # Make sure we round up
547 a8083063 Iustin Pop
  if int(value) < value:
548 a8083063 Iustin Pop
    value += 1
549 a8083063 Iustin Pop
550 a8083063 Iustin Pop
  # Round up to the next multiple of 4
551 a8083063 Iustin Pop
  value = int(value)
552 a8083063 Iustin Pop
  if value % 4:
553 a8083063 Iustin Pop
    value += 4 - value % 4
554 a8083063 Iustin Pop
555 a8083063 Iustin Pop
  return value
556 a8083063 Iustin Pop
557 a8083063 Iustin Pop
558 a8083063 Iustin Pop
def AddAuthorizedKey(file_name, key):
559 a8083063 Iustin Pop
  """Adds an SSH public key to an authorized_keys file.
560 a8083063 Iustin Pop

561 a8083063 Iustin Pop
  Args:
562 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
563 a8083063 Iustin Pop
    key: String containing key
564 a8083063 Iustin Pop
  """
565 a8083063 Iustin Pop
  key_fields = key.split()
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
  f = open(file_name, 'a+')
568 a8083063 Iustin Pop
  try:
569 a8083063 Iustin Pop
    nl = True
570 a8083063 Iustin Pop
    for line in f:
571 a8083063 Iustin Pop
      # Ignore whitespace changes
572 a8083063 Iustin Pop
      if line.split() == key_fields:
573 a8083063 Iustin Pop
        break
574 a8083063 Iustin Pop
      nl = line.endswith('\n')
575 a8083063 Iustin Pop
    else:
576 a8083063 Iustin Pop
      if not nl:
577 a8083063 Iustin Pop
        f.write("\n")
578 a8083063 Iustin Pop
      f.write(key.rstrip('\r\n'))
579 a8083063 Iustin Pop
      f.write("\n")
580 a8083063 Iustin Pop
      f.flush()
581 a8083063 Iustin Pop
  finally:
582 a8083063 Iustin Pop
    f.close()
583 a8083063 Iustin Pop
584 a8083063 Iustin Pop
585 a8083063 Iustin Pop
def RemoveAuthorizedKey(file_name, key):
586 a8083063 Iustin Pop
  """Removes an SSH public key from an authorized_keys file.
587 a8083063 Iustin Pop

588 a8083063 Iustin Pop
  Args:
589 a8083063 Iustin Pop
    file_name: Path to authorized_keys file
590 a8083063 Iustin Pop
    key: String containing key
591 a8083063 Iustin Pop
  """
592 a8083063 Iustin Pop
  key_fields = key.split()
593 a8083063 Iustin Pop
594 a8083063 Iustin Pop
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
595 a8083063 Iustin Pop
  try:
596 59f82e3f Michael Hanselmann
    out = os.fdopen(fd, 'w')
597 a8083063 Iustin Pop
    try:
598 59f82e3f Michael Hanselmann
      f = open(file_name, 'r')
599 59f82e3f Michael Hanselmann
      try:
600 59f82e3f Michael Hanselmann
        for line in f:
601 59f82e3f Michael Hanselmann
          # Ignore whitespace changes while comparing lines
602 59f82e3f Michael Hanselmann
          if line.split() != key_fields:
603 59f82e3f Michael Hanselmann
            out.write(line)
604 899d2a81 Michael Hanselmann
605 899d2a81 Michael Hanselmann
        out.flush()
606 899d2a81 Michael Hanselmann
        os.rename(tmpname, file_name)
607 899d2a81 Michael Hanselmann
      finally:
608 899d2a81 Michael Hanselmann
        f.close()
609 899d2a81 Michael Hanselmann
    finally:
610 899d2a81 Michael Hanselmann
      out.close()
611 899d2a81 Michael Hanselmann
  except:
612 899d2a81 Michael Hanselmann
    RemoveFile(tmpname)
613 899d2a81 Michael Hanselmann
    raise
614 899d2a81 Michael Hanselmann
615 899d2a81 Michael Hanselmann
616 9440aeab Michael Hanselmann
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
617 9440aeab Michael Hanselmann
  """Sets the name of an IP address and hostname in /etc/hosts.
618 899d2a81 Michael Hanselmann

619 899d2a81 Michael Hanselmann
  """
620 7fbb1f65 Michael Hanselmann
  # Ensure aliases are unique
621 7fbb1f65 Michael Hanselmann
  aliases = UniqueSequence([hostname] + aliases)[1:]
622 7fbb1f65 Michael Hanselmann
623 9440aeab Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
624 899d2a81 Michael Hanselmann
  try:
625 9440aeab Michael Hanselmann
    out = os.fdopen(fd, 'w')
626 9440aeab Michael Hanselmann
    try:
627 9440aeab Michael Hanselmann
      f = open(file_name, 'r')
628 9440aeab Michael Hanselmann
      try:
629 9440aeab Michael Hanselmann
        written = False
630 9440aeab Michael Hanselmann
        for line in f:
631 9440aeab Michael Hanselmann
          fields = line.split()
632 7e3dbb94 Iustin Pop
          if fields and not fields[0].startswith('#') and ip == fields[0]:
633 9440aeab Michael Hanselmann
            continue
634 9440aeab Michael Hanselmann
          out.write(line)
635 9440aeab Michael Hanselmann
636 7e3dbb94 Iustin Pop
        out.write("%s\t%s" % (ip, hostname))
637 9440aeab Michael Hanselmann
        if aliases:
638 9440aeab Michael Hanselmann
          out.write(" %s" % ' '.join(aliases))
639 9440aeab Michael Hanselmann
        out.write('\n')
640 9440aeab Michael Hanselmann
641 9440aeab Michael Hanselmann
        out.flush()
642 2e3e75b7 Michael Hanselmann
        os.fsync(out)
643 9440aeab Michael Hanselmann
        os.rename(tmpname, file_name)
644 9440aeab Michael Hanselmann
      finally:
645 9440aeab Michael Hanselmann
        f.close()
646 9440aeab Michael Hanselmann
    finally:
647 9440aeab Michael Hanselmann
      out.close()
648 9440aeab Michael Hanselmann
  except:
649 9440aeab Michael Hanselmann
    RemoveFile(tmpname)
650 9440aeab Michael Hanselmann
    raise
651 899d2a81 Michael Hanselmann
652 899d2a81 Michael Hanselmann
653 d9c02ca6 Michael Hanselmann
def AddHostToEtcHosts(hostname):
654 d9c02ca6 Michael Hanselmann
  """Wrapper around SetEtcHostsEntry.
655 d9c02ca6 Michael Hanselmann

656 d9c02ca6 Michael Hanselmann
  """
657 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
658 d9c02ca6 Michael Hanselmann
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
659 d9c02ca6 Michael Hanselmann
660 d9c02ca6 Michael Hanselmann
661 899d2a81 Michael Hanselmann
def RemoveEtcHostsEntry(file_name, hostname):
662 3e1cdf9f Michael Hanselmann
  """Removes a hostname from /etc/hosts.
663 899d2a81 Michael Hanselmann

664 9440aeab Michael Hanselmann
  IP addresses without names are removed from the file.
665 899d2a81 Michael Hanselmann
  """
666 899d2a81 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
667 899d2a81 Michael Hanselmann
  try:
668 899d2a81 Michael Hanselmann
    out = os.fdopen(fd, 'w')
669 899d2a81 Michael Hanselmann
    try:
670 899d2a81 Michael Hanselmann
      f = open(file_name, 'r')
671 899d2a81 Michael Hanselmann
      try:
672 899d2a81 Michael Hanselmann
        for line in f:
673 899d2a81 Michael Hanselmann
          fields = line.split()
674 899d2a81 Michael Hanselmann
          if len(fields) > 1 and not fields[0].startswith('#'):
675 899d2a81 Michael Hanselmann
            names = fields[1:]
676 899d2a81 Michael Hanselmann
            if hostname in names:
677 899d2a81 Michael Hanselmann
              while hostname in names:
678 899d2a81 Michael Hanselmann
                names.remove(hostname)
679 899d2a81 Michael Hanselmann
              if names:
680 9440aeab Michael Hanselmann
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
681 899d2a81 Michael Hanselmann
              continue
682 899d2a81 Michael Hanselmann
683 899d2a81 Michael Hanselmann
          out.write(line)
684 59f82e3f Michael Hanselmann
685 59f82e3f Michael Hanselmann
        out.flush()
686 2e3e75b7 Michael Hanselmann
        os.fsync(out)
687 59f82e3f Michael Hanselmann
        os.rename(tmpname, file_name)
688 59f82e3f Michael Hanselmann
      finally:
689 59f82e3f Michael Hanselmann
        f.close()
690 a8083063 Iustin Pop
    finally:
691 59f82e3f Michael Hanselmann
      out.close()
692 59f82e3f Michael Hanselmann
  except:
693 59f82e3f Michael Hanselmann
    RemoveFile(tmpname)
694 59f82e3f Michael Hanselmann
    raise
695 a8083063 Iustin Pop
696 a8083063 Iustin Pop
697 d9c02ca6 Michael Hanselmann
def RemoveHostFromEtcHosts(hostname):
698 d9c02ca6 Michael Hanselmann
  """Wrapper around RemoveEtcHostsEntry.
699 d9c02ca6 Michael Hanselmann

700 d9c02ca6 Michael Hanselmann
  """
701 d9c02ca6 Michael Hanselmann
  hi = HostInfo(name=hostname)
702 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
703 d9c02ca6 Michael Hanselmann
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
704 d9c02ca6 Michael Hanselmann
705 d9c02ca6 Michael Hanselmann
706 a8083063 Iustin Pop
def CreateBackup(file_name):
707 a8083063 Iustin Pop
  """Creates a backup of a file.
708 a8083063 Iustin Pop

709 a8083063 Iustin Pop
  Returns: the path to the newly created backup file.
710 a8083063 Iustin Pop

711 a8083063 Iustin Pop
  """
712 a8083063 Iustin Pop
  if not os.path.isfile(file_name):
713 3ecf6786 Iustin Pop
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
714 3ecf6786 Iustin Pop
                                file_name)
715 a8083063 Iustin Pop
716 081b1e69 Michael Hanselmann
  prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
717 65fe4693 Iustin Pop
  dir_name = os.path.dirname(file_name)
718 081b1e69 Michael Hanselmann
719 081b1e69 Michael Hanselmann
  fsrc = open(file_name, 'rb')
720 081b1e69 Michael Hanselmann
  try:
721 65fe4693 Iustin Pop
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
722 081b1e69 Michael Hanselmann
    fdst = os.fdopen(fd, 'wb')
723 081b1e69 Michael Hanselmann
    try:
724 081b1e69 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
725 081b1e69 Michael Hanselmann
    finally:
726 081b1e69 Michael Hanselmann
      fdst.close()
727 081b1e69 Michael Hanselmann
  finally:
728 081b1e69 Michael Hanselmann
    fsrc.close()
729 081b1e69 Michael Hanselmann
730 a8083063 Iustin Pop
  return backup_name
731 a8083063 Iustin Pop
732 a8083063 Iustin Pop
733 a8083063 Iustin Pop
def ShellQuote(value):
734 a8083063 Iustin Pop
  """Quotes shell argument according to POSIX.
735 3ecf6786 Iustin Pop

736 a8083063 Iustin Pop
  """
737 a8083063 Iustin Pop
  if _re_shell_unquoted.match(value):
738 a8083063 Iustin Pop
    return value
739 a8083063 Iustin Pop
  else:
740 a8083063 Iustin Pop
    return "'%s'" % value.replace("'", "'\\''")
741 a8083063 Iustin Pop
742 a8083063 Iustin Pop
743 a8083063 Iustin Pop
def ShellQuoteArgs(args):
744 a8083063 Iustin Pop
  """Quotes all given shell arguments and concatenates using spaces.
745 a8083063 Iustin Pop

746 a8083063 Iustin Pop
  """
747 a8083063 Iustin Pop
  return ' '.join([ShellQuote(i) for i in args])
748 88d14415 Michael Hanselmann
749 88d14415 Michael Hanselmann
750 b15d625f Iustin Pop
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
751 2c30e9d7 Alexander Schreiber
  """Simple ping implementation using TCP connect(2).
752 2c30e9d7 Alexander Schreiber

753 b15d625f Iustin Pop
  Try to do a TCP connect(2) from an optional source IP to the
754 b15d625f Iustin Pop
  specified target IP and the specified target port. If the optional
755 b15d625f Iustin Pop
  parameter live_port_needed is set to true, requires the remote end
756 b15d625f Iustin Pop
  to accept the connection. The timeout is specified in seconds and
757 b15d625f Iustin Pop
  defaults to 10 seconds. If the source optional argument is not
758 b15d625f Iustin Pop
  passed, the source address selection is left to the kernel,
759 b15d625f Iustin Pop
  otherwise we try to connect using the passed address (failures to
760 b15d625f Iustin Pop
  bind other than EADDRNOTAVAIL will be ignored).
761 2c30e9d7 Alexander Schreiber

762 2c30e9d7 Alexander Schreiber
  """
763 2c30e9d7 Alexander Schreiber
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
764 2c30e9d7 Alexander Schreiber
765 2c30e9d7 Alexander Schreiber
  sucess = False
766 2c30e9d7 Alexander Schreiber
767 b15d625f Iustin Pop
  if source is not None:
768 b15d625f Iustin Pop
    try:
769 b15d625f Iustin Pop
      sock.bind((source, 0))
770 b15d625f Iustin Pop
    except socket.error, (errcode, errstring):
771 b15d625f Iustin Pop
      if errcode == errno.EADDRNOTAVAIL:
772 b15d625f Iustin Pop
        success = False
773 2c30e9d7 Alexander Schreiber
774 2c30e9d7 Alexander Schreiber
  sock.settimeout(timeout)
775 2c30e9d7 Alexander Schreiber
776 2c30e9d7 Alexander Schreiber
  try:
777 2c30e9d7 Alexander Schreiber
    sock.connect((target, port))
778 2c30e9d7 Alexander Schreiber
    sock.close()
779 2c30e9d7 Alexander Schreiber
    success = True
780 2c30e9d7 Alexander Schreiber
  except socket.timeout:
781 2c30e9d7 Alexander Schreiber
    success = False
782 2c30e9d7 Alexander Schreiber
  except socket.error, (errcode, errstring):
783 4ca1b175 Alexander Schreiber
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
784 2c30e9d7 Alexander Schreiber
785 2c30e9d7 Alexander Schreiber
  return success
786 eedbda4b Michael Hanselmann
787 eedbda4b Michael Hanselmann
788 eedbda4b Michael Hanselmann
def ListVisibleFiles(path):
789 eedbda4b Michael Hanselmann
  """Returns a list of all visible files in a directory.
790 eedbda4b Michael Hanselmann

791 eedbda4b Michael Hanselmann
  """
792 f3299a07 Michael Hanselmann
  files = [i for i in os.listdir(path) if not i.startswith(".")]
793 f3299a07 Michael Hanselmann
  files.sort()
794 f3299a07 Michael Hanselmann
  return files
795 2f8b60b3 Iustin Pop
796 2f8b60b3 Iustin Pop
797 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
798 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
799 257f4c0a Iustin Pop

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

804 2f8b60b3 Iustin Pop
  """
805 2f8b60b3 Iustin Pop
  try:
806 257f4c0a Iustin Pop
    if isinstance(user, basestring):
807 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
808 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
809 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
810 257f4c0a Iustin Pop
    else:
811 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
812 257f4c0a Iustin Pop
                                   type(user))
813 2f8b60b3 Iustin Pop
  except KeyError:
814 2f8b60b3 Iustin Pop
    return default
815 2f8b60b3 Iustin Pop
  return result.pw_dir
816 59072e7e Michael Hanselmann
817 59072e7e Michael Hanselmann
818 24818e8f Michael Hanselmann
def NewUUID():
819 59072e7e Michael Hanselmann
  """Returns a random UUID.
820 59072e7e Michael Hanselmann

821 59072e7e Michael Hanselmann
  """
822 59072e7e Michael Hanselmann
  f = open("/proc/sys/kernel/random/uuid", "r")
823 59072e7e Michael Hanselmann
  try:
824 59072e7e Michael Hanselmann
    return f.read(128).rstrip("\n")
825 59072e7e Michael Hanselmann
  finally:
826 59072e7e Michael Hanselmann
    f.close()
827 087b34fe Iustin Pop
828 087b34fe Iustin Pop
829 087b34fe Iustin Pop
def WriteFile(file_name, fn=None, data=None,
830 087b34fe Iustin Pop
              mode=None, uid=-1, gid=-1,
831 71714516 Michael Hanselmann
              atime=None, mtime=None, close=True,
832 04a8d789 Michael Hanselmann
              dry_run=False, backup=False,
833 71714516 Michael Hanselmann
              prewrite=None, postwrite=None):
834 087b34fe Iustin Pop
  """(Over)write a file atomically.
835 087b34fe Iustin Pop

836 087b34fe Iustin Pop
  The file_name and either fn (a function taking one argument, the
837 087b34fe Iustin Pop
  file descriptor, and which should write the data to it) or data (the
838 087b34fe Iustin Pop
  contents of the file) must be passed. The other arguments are
839 087b34fe Iustin Pop
  optional and allow setting the file mode, owner and group, and the
840 087b34fe Iustin Pop
  mtime/atime of the file.
841 087b34fe Iustin Pop

842 087b34fe Iustin Pop
  If the function doesn't raise an exception, it has succeeded and the
843 087b34fe Iustin Pop
  target file has the new contents. If the file has raised an
844 087b34fe Iustin Pop
  exception, an existing target file should be unmodified and the
845 087b34fe Iustin Pop
  temporary file should be removed.
846 087b34fe Iustin Pop

847 71714516 Michael Hanselmann
  Args:
848 71714516 Michael Hanselmann
    file_name: New filename
849 71714516 Michael Hanselmann
    fn: Content writing function, called with file descriptor as parameter
850 71714516 Michael Hanselmann
    data: Content as string
851 71714516 Michael Hanselmann
    mode: File mode
852 71714516 Michael Hanselmann
    uid: Owner
853 71714516 Michael Hanselmann
    gid: Group
854 71714516 Michael Hanselmann
    atime: Access time
855 71714516 Michael Hanselmann
    mtime: Modification time
856 71714516 Michael Hanselmann
    close: Whether to close file after writing it
857 71714516 Michael Hanselmann
    prewrite: Function object called before writing content
858 71714516 Michael Hanselmann
    postwrite: Function object called after writing content
859 71714516 Michael Hanselmann

860 71714516 Michael Hanselmann
  Returns:
861 71714516 Michael Hanselmann
    None if "close" parameter evaluates to True, otherwise file descriptor.
862 71714516 Michael Hanselmann

863 087b34fe Iustin Pop
  """
864 04a8d789 Michael Hanselmann
  if not os.path.isabs(file_name):
865 087b34fe Iustin Pop
    raise errors.ProgrammerError("Path passed to WriteFile is not"
866 087b34fe Iustin Pop
                                 " absolute: '%s'" % file_name)
867 087b34fe Iustin Pop
868 087b34fe Iustin Pop
  if [fn, data].count(None) != 1:
869 087b34fe Iustin Pop
    raise errors.ProgrammerError("fn or data required")
870 087b34fe Iustin Pop
871 087b34fe Iustin Pop
  if [atime, mtime].count(None) == 1:
872 087b34fe Iustin Pop
    raise errors.ProgrammerError("Both atime and mtime must be either"
873 087b34fe Iustin Pop
                                 " set or None")
874 087b34fe Iustin Pop
875 70f4497c Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
876 70f4497c Michael Hanselmann
    CreateBackup(file_name)
877 087b34fe Iustin Pop
878 087b34fe Iustin Pop
  dir_name, base_name = os.path.split(file_name)
879 087b34fe Iustin Pop
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
880 087b34fe Iustin Pop
  # here we need to make sure we remove the temp file, if any error
881 087b34fe Iustin Pop
  # leaves it in place
882 087b34fe Iustin Pop
  try:
883 087b34fe Iustin Pop
    if uid != -1 or gid != -1:
884 087b34fe Iustin Pop
      os.chown(new_name, uid, gid)
885 087b34fe Iustin Pop
    if mode:
886 087b34fe Iustin Pop
      os.chmod(new_name, mode)
887 71714516 Michael Hanselmann
    if callable(prewrite):
888 71714516 Michael Hanselmann
      prewrite(fd)
889 087b34fe Iustin Pop
    if data is not None:
890 087b34fe Iustin Pop
      os.write(fd, data)
891 087b34fe Iustin Pop
    else:
892 087b34fe Iustin Pop
      fn(fd)
893 71714516 Michael Hanselmann
    if callable(postwrite):
894 71714516 Michael Hanselmann
      postwrite(fd)
895 087b34fe Iustin Pop
    os.fsync(fd)
896 087b34fe Iustin Pop
    if atime is not None and mtime is not None:
897 087b34fe Iustin Pop
      os.utime(new_name, (atime, mtime))
898 70f4497c Michael Hanselmann
    if not dry_run:
899 70f4497c Michael Hanselmann
      os.rename(new_name, file_name)
900 087b34fe Iustin Pop
  finally:
901 71714516 Michael Hanselmann
    if close:
902 71714516 Michael Hanselmann
      os.close(fd)
903 71714516 Michael Hanselmann
      result = None
904 71714516 Michael Hanselmann
    else:
905 71714516 Michael Hanselmann
      result = fd
906 087b34fe Iustin Pop
    RemoveFile(new_name)
907 78feb6fb Guido Trotter
908 71714516 Michael Hanselmann
  return result
909 71714516 Michael Hanselmann
910 78feb6fb Guido Trotter
911 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
912 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
913 7b4126b7 Iustin Pop

914 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
915 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
916 7b4126b7 Iustin Pop
  value, the index will be returned.
917 7b4126b7 Iustin Pop

918 7b4126b7 Iustin Pop
  The base argument is used to start at a different offset,
919 7b4126b7 Iustin Pop
  i.e. [3, 4, 6] with offset=3 will return 5.
920 7b4126b7 Iustin Pop

921 7b4126b7 Iustin Pop
  Example: [0, 1, 3] will return 2.
922 7b4126b7 Iustin Pop

923 7b4126b7 Iustin Pop
  """
924 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
925 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
926 7b4126b7 Iustin Pop
    if elem > idx + base:
927 7b4126b7 Iustin Pop
      # idx is not used
928 7b4126b7 Iustin Pop
      return idx + base
929 7b4126b7 Iustin Pop
  return None
930 7b4126b7 Iustin Pop
931 7b4126b7 Iustin Pop
932 78feb6fb Guido Trotter
def all(seq, pred=bool):
933 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for every element in the iterable"
934 78feb6fb Guido Trotter
  for elem in itertools.ifilterfalse(pred, seq):
935 78feb6fb Guido Trotter
    return False
936 78feb6fb Guido Trotter
  return True
937 78feb6fb Guido Trotter
938 78feb6fb Guido Trotter
939 78feb6fb Guido Trotter
def any(seq, pred=bool):
940 78feb6fb Guido Trotter
  "Returns True if pred(x) is True for at least one element in the iterable"
941 78feb6fb Guido Trotter
  for elem in itertools.ifilter(pred, seq):
942 78feb6fb Guido Trotter
    return True
943 78feb6fb Guido Trotter
  return False
944 f7414041 Michael Hanselmann
945 f7414041 Michael Hanselmann
946 f7414041 Michael Hanselmann
def UniqueSequence(seq):
947 f7414041 Michael Hanselmann
  """Returns a list with unique elements.
948 f7414041 Michael Hanselmann

949 f7414041 Michael Hanselmann
  Element order is preserved.
950 f7414041 Michael Hanselmann
  """
951 f7414041 Michael Hanselmann
  seen = set()
952 f7414041 Michael Hanselmann
  return [i for i in seq if i not in seen and not seen.add(i)]
953 1862d460 Alexander Schreiber
954 1862d460 Alexander Schreiber
955 1862d460 Alexander Schreiber
def IsValidMac(mac):
956 1862d460 Alexander Schreiber
  """Predicate to check if a MAC address is valid.
957 1862d460 Alexander Schreiber

958 1862d460 Alexander Schreiber
  Checks wether the supplied MAC address is formally correct, only
959 1862d460 Alexander Schreiber
  accepts colon separated format.
960 1862d460 Alexander Schreiber
  """
961 1862d460 Alexander Schreiber
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
962 1862d460 Alexander Schreiber
  return mac_check.match(mac) is not None
963 06009e27 Iustin Pop
964 06009e27 Iustin Pop
965 06009e27 Iustin Pop
def TestDelay(duration):
966 06009e27 Iustin Pop
  """Sleep for a fixed amount of time.
967 06009e27 Iustin Pop

968 06009e27 Iustin Pop
  """
969 06009e27 Iustin Pop
  if duration < 0:
970 06009e27 Iustin Pop
    return False
971 06009e27 Iustin Pop
  time.sleep(duration)
972 06009e27 Iustin Pop
  return True
973 8f765069 Iustin Pop
974 8f765069 Iustin Pop
975 8ff612c2 Iustin Pop
def Daemonize(logfile, noclose_fds=None):
976 8f765069 Iustin Pop
  """Daemonize the current process.
977 8f765069 Iustin Pop

978 8f765069 Iustin Pop
  This detaches the current process from the controlling terminal and
979 8f765069 Iustin Pop
  runs it in the background as a daemon.
980 8f765069 Iustin Pop

981 8f765069 Iustin Pop
  """
982 8f765069 Iustin Pop
  UMASK = 077
983 8f765069 Iustin Pop
  WORKDIR = "/"
984 8f765069 Iustin Pop
  # Default maximum for the number of available file descriptors.
985 8f765069 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
986 8f765069 Iustin Pop
    try:
987 8f765069 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
988 8f765069 Iustin Pop
      if MAXFD < 0:
989 8f765069 Iustin Pop
        MAXFD = 1024
990 8f765069 Iustin Pop
    except OSError:
991 8f765069 Iustin Pop
      MAXFD = 1024
992 8f765069 Iustin Pop
  else:
993 8f765069 Iustin Pop
    MAXFD = 1024
994 8f765069 Iustin Pop
995 8f765069 Iustin Pop
  # this might fail
996 8f765069 Iustin Pop
  pid = os.fork()
997 8f765069 Iustin Pop
  if (pid == 0):  # The first child.
998 8f765069 Iustin Pop
    os.setsid()
999 8f765069 Iustin Pop
    # this might fail
1000 8f765069 Iustin Pop
    pid = os.fork() # Fork a second child.
1001 8f765069 Iustin Pop
    if (pid == 0):  # The second child.
1002 8f765069 Iustin Pop
      os.chdir(WORKDIR)
1003 8f765069 Iustin Pop
      os.umask(UMASK)
1004 8f765069 Iustin Pop
    else:
1005 8f765069 Iustin Pop
      # exit() or _exit()?  See below.
1006 8f765069 Iustin Pop
      os._exit(0) # Exit parent (the first child) of the second child.
1007 8f765069 Iustin Pop
  else:
1008 8f765069 Iustin Pop
    os._exit(0) # Exit parent of the first child.
1009 8f765069 Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1010 8f765069 Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
1011 8f765069 Iustin Pop
    maxfd = MAXFD
1012 8f765069 Iustin Pop
1013 8f765069 Iustin Pop
  # Iterate through and close all file descriptors.
1014 8f765069 Iustin Pop
  for fd in range(0, maxfd):
1015 8ff612c2 Iustin Pop
    if noclose_fds and fd in noclose_fds:
1016 8ff612c2 Iustin Pop
      continue
1017 8f765069 Iustin Pop
    try:
1018 8f765069 Iustin Pop
      os.close(fd)
1019 8f765069 Iustin Pop
    except OSError: # ERROR, fd wasn't open to begin with (ignored)
1020 8f765069 Iustin Pop
      pass
1021 8f765069 Iustin Pop
  os.open(logfile, os.O_RDWR|os.O_CREAT|os.O_APPEND, 0600)
1022 8f765069 Iustin Pop
  # Duplicate standard input to standard output and standard error.
1023 8f765069 Iustin Pop
  os.dup2(0, 1)     # standard output (1)
1024 8f765069 Iustin Pop
  os.dup2(0, 2)     # standard error (2)
1025 8f765069 Iustin Pop
  return 0
1026 57c177af Iustin Pop
1027 57c177af Iustin Pop
1028 57c177af Iustin Pop
def FindFile(name, search_path, test=os.path.exists):
1029 57c177af Iustin Pop
  """Look for a filesystem object in a given path.
1030 57c177af Iustin Pop

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

1034 57c177af Iustin Pop
  Args:
1035 57c177af Iustin Pop
    - name: the name to look for
1036 57c177af Iustin Pop
    - search_path: list of directory names
1037 57c177af Iustin Pop
    - test: the test which the full path must satisfy
1038 57c177af Iustin Pop
      (defaults to os.path.exists)
1039 57c177af Iustin Pop

1040 57c177af Iustin Pop
  Returns:
1041 57c177af Iustin Pop
    - full path to the item if found
1042 57c177af Iustin Pop
    - None otherwise
1043 57c177af Iustin Pop

1044 57c177af Iustin Pop
  """
1045 57c177af Iustin Pop
  for dir_name in search_path:
1046 57c177af Iustin Pop
    item_name = os.path.sep.join([dir_name, name])
1047 57c177af Iustin Pop
    if test(item_name):
1048 57c177af Iustin Pop
      return item_name
1049 57c177af Iustin Pop
  return None
1050 8d1a2a64 Michael Hanselmann
1051 8d1a2a64 Michael Hanselmann
1052 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
1053 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
1054 8d1a2a64 Michael Hanselmann

1055 8d1a2a64 Michael Hanselmann
  A non-None return value means there's an error, and the return value
1056 8d1a2a64 Michael Hanselmann
  is the error message.
1057 8d1a2a64 Michael Hanselmann

1058 8d1a2a64 Michael Hanselmann
  """
1059 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
1060 8d1a2a64 Michael Hanselmann
  if vgsize is None:
1061 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
1062 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
1063 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1064 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
1065 8d1a2a64 Michael Hanselmann
  return None
1066 7996a135 Iustin Pop
1067 7996a135 Iustin Pop
1068 7996a135 Iustin Pop
def LockedMethod(fn):
1069 7996a135 Iustin Pop
  """Synchronized object access decorator.
1070 7996a135 Iustin Pop

1071 7996a135 Iustin Pop
  This decorator is intended to protect access to an object using the
1072 7996a135 Iustin Pop
  object's own lock which is hardcoded to '_lock'.
1073 7996a135 Iustin Pop

1074 7996a135 Iustin Pop
  """
1075 7996a135 Iustin Pop
  def wrapper(self, *args, **kwargs):
1076 7996a135 Iustin Pop
    assert hasattr(self, '_lock')
1077 7996a135 Iustin Pop
    lock = self._lock
1078 7996a135 Iustin Pop
    lock.acquire()
1079 7996a135 Iustin Pop
    try:
1080 7996a135 Iustin Pop
      result = fn(self, *args, **kwargs)
1081 7996a135 Iustin Pop
    finally:
1082 7996a135 Iustin Pop
      lock.release()
1083 7996a135 Iustin Pop
    return result
1084 7996a135 Iustin Pop
  return wrapper