Statistics
| Branch: | Tag: | Revision:

root / lib / utils.py @ 506be7c5

History | View | Annotate | Download (79.8 kB)

1
#
2
#
3

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

    
21

    
22
"""Ganeti utility module.
23

24
This module holds functions that can be used in both daemons (all) and
25
the command line scripts.
26

27
"""
28

    
29

    
30
import os
31
import time
32
import subprocess
33
import re
34
import socket
35
import tempfile
36
import shutil
37
import errno
38
import pwd
39
import itertools
40
import select
41
import fcntl
42
import resource
43
import logging
44
import logging.handlers
45
import signal
46
import datetime
47
import calendar
48
import collections
49
import struct
50
import IN
51

    
52
from cStringIO import StringIO
53

    
54
try:
55
  from hashlib import sha1
56
except ImportError:
57
  import sha
58
  sha1 = sha.new
59

    
60
from ganeti import errors
61
from ganeti import constants
62

    
63

    
64
_locksheld = []
65
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
66

    
67
debug_locks = False
68

    
69
#: when set to True, L{RunCmd} is disabled
70
no_fork = False
71

    
72
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
73

    
74
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
75
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
76
#
77
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
78
# pid_t to "int".
79
#
80
# IEEE Std 1003.1-2008:
81
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
82
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
83
_STRUCT_UCRED = "iII"
84
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
85

    
86

    
87
class RunResult(object):
88
  """Holds the result of running external programs.
89

90
  @type exit_code: int
91
  @ivar exit_code: the exit code of the program, or None (if the program
92
      didn't exit())
93
  @type signal: int or None
94
  @ivar signal: the signal that caused the program to finish, or None
95
      (if the program wasn't terminated by a signal)
96
  @type stdout: str
97
  @ivar stdout: the standard output of the program
98
  @type stderr: str
99
  @ivar stderr: the standard error of the program
100
  @type failed: boolean
101
  @ivar failed: True in case the program was
102
      terminated by a signal or exited with a non-zero exit code
103
  @ivar fail_reason: a string detailing the termination reason
104

105
  """
106
  __slots__ = ["exit_code", "signal", "stdout", "stderr",
107
               "failed", "fail_reason", "cmd"]
108

    
109

    
110
  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
111
    self.cmd = cmd
112
    self.exit_code = exit_code
113
    self.signal = signal_
114
    self.stdout = stdout
115
    self.stderr = stderr
116
    self.failed = (signal_ is not None or exit_code != 0)
117

    
118
    if self.signal is not None:
119
      self.fail_reason = "terminated by signal %s" % self.signal
120
    elif self.exit_code is not None:
121
      self.fail_reason = "exited with exit code %s" % self.exit_code
122
    else:
123
      self.fail_reason = "unable to determine termination reason"
124

    
125
    if self.failed:
126
      logging.debug("Command '%s' failed (%s); output: %s",
127
                    self.cmd, self.fail_reason, self.output)
128

    
129
  def _GetOutput(self):
130
    """Returns the combined stdout and stderr for easier usage.
131

132
    """
133
    return self.stdout + self.stderr
134

    
135
  output = property(_GetOutput, None, None, "Return full output")
136

    
137

    
138
def RunCmd(cmd, env=None, output=None, cwd='/', reset_env=False):
139
  """Execute a (shell) command.
140

141
  The command should not read from its standard input, as it will be
142
  closed.
143

144
  @type cmd: string or list
145
  @param cmd: Command to run
146
  @type env: dict
147
  @param env: Additional environment
148
  @type output: str
149
  @param output: if desired, the output of the command can be
150
      saved in a file instead of the RunResult instance; this
151
      parameter denotes the file name (if not None)
152
  @type cwd: string
153
  @param cwd: if specified, will be used as the working
154
      directory for the command; the default will be /
155
  @type reset_env: boolean
156
  @param reset_env: whether to reset or keep the default os environment
157
  @rtype: L{RunResult}
158
  @return: RunResult instance
159
  @raise errors.ProgrammerError: if we call this when forks are disabled
160

161
  """
162
  if no_fork:
163
    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
164

    
165
  if isinstance(cmd, list):
166
    cmd = [str(val) for val in cmd]
167
    strcmd = " ".join(cmd)
168
    shell = False
169
  else:
170
    strcmd = cmd
171
    shell = True
172
  logging.debug("RunCmd '%s'", strcmd)
173

    
174
  if not reset_env:
175
    cmd_env = os.environ.copy()
176
    cmd_env["LC_ALL"] = "C"
177
  else:
178
    cmd_env = {}
179

    
180
  if env is not None:
181
    cmd_env.update(env)
182

    
183
  try:
184
    if output is None:
185
      out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd)
186
    else:
187
      status = _RunCmdFile(cmd, cmd_env, shell, output, cwd)
188
      out = err = ""
189
  except OSError, err:
190
    if err.errno == errno.ENOENT:
191
      raise errors.OpExecError("Can't execute '%s': not found (%s)" %
192
                               (strcmd, err))
193
    else:
194
      raise
195

    
196
  if status >= 0:
197
    exitcode = status
198
    signal_ = None
199
  else:
200
    exitcode = None
201
    signal_ = -status
202

    
203
  return RunResult(exitcode, signal_, out, err, strcmd)
204

    
205

    
206
def _RunCmdPipe(cmd, env, via_shell, cwd):
207
  """Run a command and return its output.
208

209
  @type  cmd: string or list
210
  @param cmd: Command to run
211
  @type env: dict
212
  @param env: The environment to use
213
  @type via_shell: bool
214
  @param via_shell: if we should run via the shell
215
  @type cwd: string
216
  @param cwd: the working directory for the program
217
  @rtype: tuple
218
  @return: (out, err, status)
219

220
  """
221
  poller = select.poll()
222
  child = subprocess.Popen(cmd, shell=via_shell,
223
                           stderr=subprocess.PIPE,
224
                           stdout=subprocess.PIPE,
225
                           stdin=subprocess.PIPE,
226
                           close_fds=True, env=env,
227
                           cwd=cwd)
228

    
229
  child.stdin.close()
230
  poller.register(child.stdout, select.POLLIN)
231
  poller.register(child.stderr, select.POLLIN)
232
  out = StringIO()
233
  err = StringIO()
234
  fdmap = {
235
    child.stdout.fileno(): (out, child.stdout),
236
    child.stderr.fileno(): (err, child.stderr),
237
    }
238
  for fd in fdmap:
239
    status = fcntl.fcntl(fd, fcntl.F_GETFL)
240
    fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
241

    
242
  while fdmap:
243
    try:
244
      pollresult = poller.poll()
245
    except EnvironmentError, eerr:
246
      if eerr.errno == errno.EINTR:
247
        continue
248
      raise
249
    except select.error, serr:
250
      if serr[0] == errno.EINTR:
251
        continue
252
      raise
253

    
254
    for fd, event in pollresult:
255
      if event & select.POLLIN or event & select.POLLPRI:
256
        data = fdmap[fd][1].read()
257
        # no data from read signifies EOF (the same as POLLHUP)
258
        if not data:
259
          poller.unregister(fd)
260
          del fdmap[fd]
261
          continue
262
        fdmap[fd][0].write(data)
263
      if (event & select.POLLNVAL or event & select.POLLHUP or
264
          event & select.POLLERR):
265
        poller.unregister(fd)
266
        del fdmap[fd]
267

    
268
  out = out.getvalue()
269
  err = err.getvalue()
270

    
271
  status = child.wait()
272
  return out, err, status
273

    
274

    
275
def _RunCmdFile(cmd, env, via_shell, output, cwd):
276
  """Run a command and save its output to a file.
277

278
  @type  cmd: string or list
279
  @param cmd: Command to run
280
  @type env: dict
281
  @param env: The environment to use
282
  @type via_shell: bool
283
  @param via_shell: if we should run via the shell
284
  @type output: str
285
  @param output: the filename in which to save the output
286
  @type cwd: string
287
  @param cwd: the working directory for the program
288
  @rtype: int
289
  @return: the exit status
290

291
  """
292
  fh = open(output, "a")
293
  try:
294
    child = subprocess.Popen(cmd, shell=via_shell,
295
                             stderr=subprocess.STDOUT,
296
                             stdout=fh,
297
                             stdin=subprocess.PIPE,
298
                             close_fds=True, env=env,
299
                             cwd=cwd)
300

    
301
    child.stdin.close()
302
    status = child.wait()
303
  finally:
304
    fh.close()
305
  return status
306

    
307

    
308
def RunParts(dir_name, env=None, reset_env=False):
309
  """Run Scripts or programs in a directory
310

311
  @type dir_name: string
312
  @param dir_name: absolute path to a directory
313
  @type env: dict
314
  @param env: The environment to use
315
  @type reset_env: boolean
316
  @param reset_env: whether to reset or keep the default os environment
317
  @rtype: list of tuples
318
  @return: list of (name, (one of RUNDIR_STATUS), RunResult)
319

320
  """
321
  rr = []
322

    
323
  try:
324
    dir_contents = ListVisibleFiles(dir_name)
325
  except OSError, err:
326
    logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
327
    return rr
328

    
329
  for relname in sorted(dir_contents):
330
    fname = PathJoin(dir_name, relname)
331
    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
332
            constants.EXT_PLUGIN_MASK.match(relname) is not None):
333
      rr.append((relname, constants.RUNPARTS_SKIP, None))
334
    else:
335
      try:
336
        result = RunCmd([fname], env=env, reset_env=reset_env)
337
      except Exception, err: # pylint: disable-msg=W0703
338
        rr.append((relname, constants.RUNPARTS_ERR, str(err)))
339
      else:
340
        rr.append((relname, constants.RUNPARTS_RUN, result))
341

    
342
  return rr
343

    
344

    
345
def GetSocketCredentials(sock):
346
  """Returns the credentials of the foreign process connected to a socket.
347

348
  @param sock: Unix socket
349
  @rtype: tuple; (number, number, number)
350
  @return: The PID, UID and GID of the connected foreign process.
351

352
  """
353
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
354
                             _STRUCT_UCRED_SIZE)
355
  return struct.unpack(_STRUCT_UCRED, peercred)
356

    
357

    
358
def RemoveFile(filename):
359
  """Remove a file ignoring some errors.
360

361
  Remove a file, ignoring non-existing ones or directories. Other
362
  errors are passed.
363

364
  @type filename: str
365
  @param filename: the file to be removed
366

367
  """
368
  try:
369
    os.unlink(filename)
370
  except OSError, err:
371
    if err.errno not in (errno.ENOENT, errno.EISDIR):
372
      raise
373

    
374

    
375
def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
376
  """Renames a file.
377

378
  @type old: string
379
  @param old: Original path
380
  @type new: string
381
  @param new: New path
382
  @type mkdir: bool
383
  @param mkdir: Whether to create target directory if it doesn't exist
384
  @type mkdir_mode: int
385
  @param mkdir_mode: Mode for newly created directories
386

387
  """
388
  try:
389
    return os.rename(old, new)
390
  except OSError, err:
391
    # In at least one use case of this function, the job queue, directory
392
    # creation is very rare. Checking for the directory before renaming is not
393
    # as efficient.
394
    if mkdir and err.errno == errno.ENOENT:
395
      # Create directory and try again
396
      Makedirs(os.path.dirname(new), mode=mkdir_mode)
397

    
398
      return os.rename(old, new)
399

    
400
    raise
401

    
402

    
403
def Makedirs(path, mode=0750):
404
  """Super-mkdir; create a leaf directory and all intermediate ones.
405

406
  This is a wrapper around C{os.makedirs} adding error handling not implemented
407
  before Python 2.5.
408

409
  """
410
  try:
411
    os.makedirs(path, mode)
412
  except OSError, err:
413
    # Ignore EEXIST. This is only handled in os.makedirs as included in
414
    # Python 2.5 and above.
415
    if err.errno != errno.EEXIST or not os.path.exists(path):
416
      raise
417

    
418

    
419
def ResetTempfileModule():
420
  """Resets the random name generator of the tempfile module.
421

422
  This function should be called after C{os.fork} in the child process to
423
  ensure it creates a newly seeded random generator. Otherwise it would
424
  generate the same random parts as the parent process. If several processes
425
  race for the creation of a temporary file, this could lead to one not getting
426
  a temporary name.
427

428
  """
429
  # pylint: disable-msg=W0212
430
  if hasattr(tempfile, "_once_lock") and hasattr(tempfile, "_name_sequence"):
431
    tempfile._once_lock.acquire()
432
    try:
433
      # Reset random name generator
434
      tempfile._name_sequence = None
435
    finally:
436
      tempfile._once_lock.release()
437
  else:
438
    logging.critical("The tempfile module misses at least one of the"
439
                     " '_once_lock' and '_name_sequence' attributes")
440

    
441

    
442
def _FingerprintFile(filename):
443
  """Compute the fingerprint of a file.
444

445
  If the file does not exist, a None will be returned
446
  instead.
447

448
  @type filename: str
449
  @param filename: the filename to checksum
450
  @rtype: str
451
  @return: the hex digest of the sha checksum of the contents
452
      of the file
453

454
  """
455
  if not (os.path.exists(filename) and os.path.isfile(filename)):
456
    return None
457

    
458
  f = open(filename)
459

    
460
  fp = sha1()
461
  while True:
462
    data = f.read(4096)
463
    if not data:
464
      break
465

    
466
    fp.update(data)
467

    
468
  return fp.hexdigest()
469

    
470

    
471
def FingerprintFiles(files):
472
  """Compute fingerprints for a list of files.
473

474
  @type files: list
475
  @param files: the list of filename to fingerprint
476
  @rtype: dict
477
  @return: a dictionary filename: fingerprint, holding only
478
      existing files
479

480
  """
481
  ret = {}
482

    
483
  for filename in files:
484
    cksum = _FingerprintFile(filename)
485
    if cksum:
486
      ret[filename] = cksum
487

    
488
  return ret
489

    
490

    
491
def ForceDictType(target, key_types, allowed_values=None):
492
  """Force the values of a dict to have certain types.
493

494
  @type target: dict
495
  @param target: the dict to update
496
  @type key_types: dict
497
  @param key_types: dict mapping target dict keys to types
498
                    in constants.ENFORCEABLE_TYPES
499
  @type allowed_values: list
500
  @keyword allowed_values: list of specially allowed values
501

502
  """
503
  if allowed_values is None:
504
    allowed_values = []
505

    
506
  if not isinstance(target, dict):
507
    msg = "Expected dictionary, got '%s'" % target
508
    raise errors.TypeEnforcementError(msg)
509

    
510
  for key in target:
511
    if key not in key_types:
512
      msg = "Unknown key '%s'" % key
513
      raise errors.TypeEnforcementError(msg)
514

    
515
    if target[key] in allowed_values:
516
      continue
517

    
518
    ktype = key_types[key]
519
    if ktype not in constants.ENFORCEABLE_TYPES:
520
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
521
      raise errors.ProgrammerError(msg)
522

    
523
    if ktype == constants.VTYPE_STRING:
524
      if not isinstance(target[key], basestring):
525
        if isinstance(target[key], bool) and not target[key]:
526
          target[key] = ''
527
        else:
528
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
529
          raise errors.TypeEnforcementError(msg)
530
    elif ktype == constants.VTYPE_BOOL:
531
      if isinstance(target[key], basestring) and target[key]:
532
        if target[key].lower() == constants.VALUE_FALSE:
533
          target[key] = False
534
        elif target[key].lower() == constants.VALUE_TRUE:
535
          target[key] = True
536
        else:
537
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
538
          raise errors.TypeEnforcementError(msg)
539
      elif target[key]:
540
        target[key] = True
541
      else:
542
        target[key] = False
543
    elif ktype == constants.VTYPE_SIZE:
544
      try:
545
        target[key] = ParseUnit(target[key])
546
      except errors.UnitParseError, err:
547
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
548
              (key, target[key], err)
549
        raise errors.TypeEnforcementError(msg)
550
    elif ktype == constants.VTYPE_INT:
551
      try:
552
        target[key] = int(target[key])
553
      except (ValueError, TypeError):
554
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
555
        raise errors.TypeEnforcementError(msg)
556

    
557

    
558
def IsProcessAlive(pid):
559
  """Check if a given pid exists on the system.
560

561
  @note: zombie status is not handled, so zombie processes
562
      will be returned as alive
563
  @type pid: int
564
  @param pid: the process ID to check
565
  @rtype: boolean
566
  @return: True if the process exists
567

568
  """
569
  if pid <= 0:
570
    return False
571

    
572
  try:
573
    os.stat("/proc/%d/status" % pid)
574
    return True
575
  except EnvironmentError, err:
576
    if err.errno in (errno.ENOENT, errno.ENOTDIR):
577
      return False
578
    raise
579

    
580

    
581
def ReadPidFile(pidfile):
582
  """Read a pid from a file.
583

584
  @type  pidfile: string
585
  @param pidfile: path to the file containing the pid
586
  @rtype: int
587
  @return: The process id, if the file exists and contains a valid PID,
588
           otherwise 0
589

590
  """
591
  try:
592
    raw_data = ReadFile(pidfile)
593
  except EnvironmentError, err:
594
    if err.errno != errno.ENOENT:
595
      logging.exception("Can't read pid file")
596
    return 0
597

    
598
  try:
599
    pid = int(raw_data)
600
  except (TypeError, ValueError), err:
601
    logging.info("Can't parse pid file contents", exc_info=True)
602
    return 0
603

    
604
  return pid
605

    
606

    
607
def MatchNameComponent(key, name_list, case_sensitive=True):
608
  """Try to match a name against a list.
609

610
  This function will try to match a name like test1 against a list
611
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
612
  this list, I{'test1'} as well as I{'test1.example'} will match, but
613
  not I{'test1.ex'}. A multiple match will be considered as no match
614
  at all (e.g. I{'test1'} against C{['test1.example.com',
615
  'test1.example.org']}), except when the key fully matches an entry
616
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
617

618
  @type key: str
619
  @param key: the name to be searched
620
  @type name_list: list
621
  @param name_list: the list of strings against which to search the key
622
  @type case_sensitive: boolean
623
  @param case_sensitive: whether to provide a case-sensitive match
624

625
  @rtype: None or str
626
  @return: None if there is no match I{or} if there are multiple matches,
627
      otherwise the element from the list which matches
628

629
  """
630
  if key in name_list:
631
    return key
632

    
633
  re_flags = 0
634
  if not case_sensitive:
635
    re_flags |= re.IGNORECASE
636
    key = key.upper()
637
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
638
  names_filtered = []
639
  string_matches = []
640
  for name in name_list:
641
    if mo.match(name) is not None:
642
      names_filtered.append(name)
643
      if not case_sensitive and key == name.upper():
644
        string_matches.append(name)
645

    
646
  if len(string_matches) == 1:
647
    return string_matches[0]
648
  if len(names_filtered) == 1:
649
    return names_filtered[0]
650
  return None
651

    
652

    
653
class HostInfo:
654
  """Class implementing resolver and hostname functionality
655

656
  """
657
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
658

    
659
  def __init__(self, name=None):
660
    """Initialize the host name object.
661

662
    If the name argument is not passed, it will use this system's
663
    name.
664

665
    """
666
    if name is None:
667
      name = self.SysName()
668

    
669
    self.query = name
670
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
671
    self.ip = self.ipaddrs[0]
672

    
673
  def ShortName(self):
674
    """Returns the hostname without domain.
675

676
    """
677
    return self.name.split('.')[0]
678

    
679
  @staticmethod
680
  def SysName():
681
    """Return the current system's name.
682

683
    This is simply a wrapper over C{socket.gethostname()}.
684

685
    """
686
    return socket.gethostname()
687

    
688
  @staticmethod
689
  def LookupHostname(hostname):
690
    """Look up hostname
691

692
    @type hostname: str
693
    @param hostname: hostname to look up
694

695
    @rtype: tuple
696
    @return: a tuple (name, aliases, ipaddrs) as returned by
697
        C{socket.gethostbyname_ex}
698
    @raise errors.ResolverError: in case of errors in resolving
699

700
    """
701
    try:
702
      result = socket.gethostbyname_ex(hostname)
703
    except socket.gaierror, err:
704
      # hostname not found in DNS
705
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
706

    
707
    return result
708

    
709
  @classmethod
710
  def NormalizeName(cls, hostname):
711
    """Validate and normalize the given hostname.
712

713
    @attention: the validation is a bit more relaxed than the standards
714
        require; most importantly, we allow underscores in names
715
    @raise errors.OpPrereqError: when the name is not valid
716

717
    """
718
    hostname = hostname.lower()
719
    if (not cls._VALID_NAME_RE.match(hostname) or
720
        # double-dots, meaning empty label
721
        ".." in hostname or
722
        # empty initial label
723
        hostname.startswith(".")):
724
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
725
                                 errors.ECODE_INVAL)
726
    if hostname.endswith("."):
727
      hostname = hostname.rstrip(".")
728
    return hostname
729

    
730

    
731
def GetHostInfo(name=None):
732
  """Lookup host name and raise an OpPrereqError for failures"""
733

    
734
  try:
735
    return HostInfo(name)
736
  except errors.ResolverError, err:
737
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
738
                               (err[0], err[2]), errors.ECODE_RESOLVER)
739

    
740

    
741
def ListVolumeGroups():
742
  """List volume groups and their size
743

744
  @rtype: dict
745
  @return:
746
       Dictionary with keys volume name and values
747
       the size of the volume
748

749
  """
750
  command = "vgs --noheadings --units m --nosuffix -o name,size"
751
  result = RunCmd(command)
752
  retval = {}
753
  if result.failed:
754
    return retval
755

    
756
  for line in result.stdout.splitlines():
757
    try:
758
      name, size = line.split()
759
      size = int(float(size))
760
    except (IndexError, ValueError), err:
761
      logging.error("Invalid output from vgs (%s): %s", err, line)
762
      continue
763

    
764
    retval[name] = size
765

    
766
  return retval
767

    
768

    
769
def BridgeExists(bridge):
770
  """Check whether the given bridge exists in the system
771

772
  @type bridge: str
773
  @param bridge: the bridge name to check
774
  @rtype: boolean
775
  @return: True if it does
776

777
  """
778
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
779

    
780

    
781
def NiceSort(name_list):
782
  """Sort a list of strings based on digit and non-digit groupings.
783

784
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
785
  will sort the list in the logical order C{['a1', 'a2', 'a10',
786
  'a11']}.
787

788
  The sort algorithm breaks each name in groups of either only-digits
789
  or no-digits. Only the first eight such groups are considered, and
790
  after that we just use what's left of the string.
791

792
  @type name_list: list
793
  @param name_list: the names to be sorted
794
  @rtype: list
795
  @return: a copy of the name list sorted with our algorithm
796

797
  """
798
  _SORTER_BASE = "(\D+|\d+)"
799
  _SORTER_FULL = "^%s%s?%s?%s?%s?%s?%s?%s?.*$" % (_SORTER_BASE, _SORTER_BASE,
800
                                                  _SORTER_BASE, _SORTER_BASE,
801
                                                  _SORTER_BASE, _SORTER_BASE,
802
                                                  _SORTER_BASE, _SORTER_BASE)
803
  _SORTER_RE = re.compile(_SORTER_FULL)
804
  _SORTER_NODIGIT = re.compile("^\D*$")
805
  def _TryInt(val):
806
    """Attempts to convert a variable to integer."""
807
    if val is None or _SORTER_NODIGIT.match(val):
808
      return val
809
    rval = int(val)
810
    return rval
811

    
812
  to_sort = [([_TryInt(grp) for grp in _SORTER_RE.match(name).groups()], name)
813
             for name in name_list]
814
  to_sort.sort()
815
  return [tup[1] for tup in to_sort]
816

    
817

    
818
def TryConvert(fn, val):
819
  """Try to convert a value ignoring errors.
820

821
  This function tries to apply function I{fn} to I{val}. If no
822
  C{ValueError} or C{TypeError} exceptions are raised, it will return
823
  the result, else it will return the original value. Any other
824
  exceptions are propagated to the caller.
825

826
  @type fn: callable
827
  @param fn: function to apply to the value
828
  @param val: the value to be converted
829
  @return: The converted value if the conversion was successful,
830
      otherwise the original value.
831

832
  """
833
  try:
834
    nv = fn(val)
835
  except (ValueError, TypeError):
836
    nv = val
837
  return nv
838

    
839

    
840
def IsValidIP(ip):
841
  """Verifies the syntax of an IPv4 address.
842

843
  This function checks if the IPv4 address passes is valid or not based
844
  on syntax (not IP range, class calculations, etc.).
845

846
  @type ip: str
847
  @param ip: the address to be checked
848
  @rtype: a regular expression match object
849
  @return: a regular expression match object, or None if the
850
      address is not valid
851

852
  """
853
  unit = "(0|[1-9]\d{0,2})"
854
  #TODO: convert and return only boolean
855
  return re.match("^%s\.%s\.%s\.%s$" % (unit, unit, unit, unit), ip)
856

    
857

    
858
def IsValidShellParam(word):
859
  """Verifies is the given word is safe from the shell's p.o.v.
860

861
  This means that we can pass this to a command via the shell and be
862
  sure that it doesn't alter the command line and is passed as such to
863
  the actual command.
864

865
  Note that we are overly restrictive here, in order to be on the safe
866
  side.
867

868
  @type word: str
869
  @param word: the word to check
870
  @rtype: boolean
871
  @return: True if the word is 'safe'
872

873
  """
874
  return bool(re.match("^[-a-zA-Z0-9._+/:%@]+$", word))
875

    
876

    
877
def BuildShellCmd(template, *args):
878
  """Build a safe shell command line from the given arguments.
879

880
  This function will check all arguments in the args list so that they
881
  are valid shell parameters (i.e. they don't contain shell
882
  metacharacters). If everything is ok, it will return the result of
883
  template % args.
884

885
  @type template: str
886
  @param template: the string holding the template for the
887
      string formatting
888
  @rtype: str
889
  @return: the expanded command line
890

891
  """
892
  for word in args:
893
    if not IsValidShellParam(word):
894
      raise errors.ProgrammerError("Shell argument '%s' contains"
895
                                   " invalid characters" % word)
896
  return template % args
897

    
898

    
899
def FormatUnit(value, units):
900
  """Formats an incoming number of MiB with the appropriate unit.
901

902
  @type value: int
903
  @param value: integer representing the value in MiB (1048576)
904
  @type units: char
905
  @param units: the type of formatting we should do:
906
      - 'h' for automatic scaling
907
      - 'm' for MiBs
908
      - 'g' for GiBs
909
      - 't' for TiBs
910
  @rtype: str
911
  @return: the formatted value (with suffix)
912

913
  """
914
  if units not in ('m', 'g', 't', 'h'):
915
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
916

    
917
  suffix = ''
918

    
919
  if units == 'm' or (units == 'h' and value < 1024):
920
    if units == 'h':
921
      suffix = 'M'
922
    return "%d%s" % (round(value, 0), suffix)
923

    
924
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
925
    if units == 'h':
926
      suffix = 'G'
927
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
928

    
929
  else:
930
    if units == 'h':
931
      suffix = 'T'
932
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
933

    
934

    
935
def ParseUnit(input_string):
936
  """Tries to extract number and scale from the given string.
937

938
  Input must be in the format C{NUMBER+ [DOT NUMBER+] SPACE*
939
  [UNIT]}. If no unit is specified, it defaults to MiB. Return value
940
  is always an int in MiB.
941

942
  """
943
  m = re.match('^([.\d]+)\s*([a-zA-Z]+)?$', str(input_string))
944
  if not m:
945
    raise errors.UnitParseError("Invalid format")
946

    
947
  value = float(m.groups()[0])
948

    
949
  unit = m.groups()[1]
950
  if unit:
951
    lcunit = unit.lower()
952
  else:
953
    lcunit = 'm'
954

    
955
  if lcunit in ('m', 'mb', 'mib'):
956
    # Value already in MiB
957
    pass
958

    
959
  elif lcunit in ('g', 'gb', 'gib'):
960
    value *= 1024
961

    
962
  elif lcunit in ('t', 'tb', 'tib'):
963
    value *= 1024 * 1024
964

    
965
  else:
966
    raise errors.UnitParseError("Unknown unit: %s" % unit)
967

    
968
  # Make sure we round up
969
  if int(value) < value:
970
    value += 1
971

    
972
  # Round up to the next multiple of 4
973
  value = int(value)
974
  if value % 4:
975
    value += 4 - value % 4
976

    
977
  return value
978

    
979

    
980
def AddAuthorizedKey(file_name, key):
981
  """Adds an SSH public key to an authorized_keys file.
982

983
  @type file_name: str
984
  @param file_name: path to authorized_keys file
985
  @type key: str
986
  @param key: string containing key
987

988
  """
989
  key_fields = key.split()
990

    
991
  f = open(file_name, 'a+')
992
  try:
993
    nl = True
994
    for line in f:
995
      # Ignore whitespace changes
996
      if line.split() == key_fields:
997
        break
998
      nl = line.endswith('\n')
999
    else:
1000
      if not nl:
1001
        f.write("\n")
1002
      f.write(key.rstrip('\r\n'))
1003
      f.write("\n")
1004
      f.flush()
1005
  finally:
1006
    f.close()
1007

    
1008

    
1009
def RemoveAuthorizedKey(file_name, key):
1010
  """Removes an SSH public key from an authorized_keys file.
1011

1012
  @type file_name: str
1013
  @param file_name: path to authorized_keys file
1014
  @type key: str
1015
  @param key: string containing key
1016

1017
  """
1018
  key_fields = key.split()
1019

    
1020
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1021
  try:
1022
    out = os.fdopen(fd, 'w')
1023
    try:
1024
      f = open(file_name, 'r')
1025
      try:
1026
        for line in f:
1027
          # Ignore whitespace changes while comparing lines
1028
          if line.split() != key_fields:
1029
            out.write(line)
1030

    
1031
        out.flush()
1032
        os.rename(tmpname, file_name)
1033
      finally:
1034
        f.close()
1035
    finally:
1036
      out.close()
1037
  except:
1038
    RemoveFile(tmpname)
1039
    raise
1040

    
1041

    
1042
def SetEtcHostsEntry(file_name, ip, hostname, aliases):
1043
  """Sets the name of an IP address and hostname in /etc/hosts.
1044

1045
  @type file_name: str
1046
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1047
  @type ip: str
1048
  @param ip: the IP address
1049
  @type hostname: str
1050
  @param hostname: the hostname to be added
1051
  @type aliases: list
1052
  @param aliases: the list of aliases to add for the hostname
1053

1054
  """
1055
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1056
  # Ensure aliases are unique
1057
  aliases = UniqueSequence([hostname] + aliases)[1:]
1058

    
1059
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1060
  try:
1061
    out = os.fdopen(fd, 'w')
1062
    try:
1063
      f = open(file_name, 'r')
1064
      try:
1065
        for line in f:
1066
          fields = line.split()
1067
          if fields and not fields[0].startswith('#') and ip == fields[0]:
1068
            continue
1069
          out.write(line)
1070

    
1071
        out.write("%s\t%s" % (ip, hostname))
1072
        if aliases:
1073
          out.write(" %s" % ' '.join(aliases))
1074
        out.write('\n')
1075

    
1076
        out.flush()
1077
        os.fsync(out)
1078
        os.chmod(tmpname, 0644)
1079
        os.rename(tmpname, file_name)
1080
      finally:
1081
        f.close()
1082
    finally:
1083
      out.close()
1084
  except:
1085
    RemoveFile(tmpname)
1086
    raise
1087

    
1088

    
1089
def AddHostToEtcHosts(hostname):
1090
  """Wrapper around SetEtcHostsEntry.
1091

1092
  @type hostname: str
1093
  @param hostname: a hostname that will be resolved and added to
1094
      L{constants.ETC_HOSTS}
1095

1096
  """
1097
  hi = HostInfo(name=hostname)
1098
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1099

    
1100

    
1101
def RemoveEtcHostsEntry(file_name, hostname):
1102
  """Removes a hostname from /etc/hosts.
1103

1104
  IP addresses without names are removed from the file.
1105

1106
  @type file_name: str
1107
  @param file_name: path to the file to modify (usually C{/etc/hosts})
1108
  @type hostname: str
1109
  @param hostname: the hostname to be removed
1110

1111
  """
1112
  # FIXME: use WriteFile + fn rather than duplicating its efforts
1113
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
1114
  try:
1115
    out = os.fdopen(fd, 'w')
1116
    try:
1117
      f = open(file_name, 'r')
1118
      try:
1119
        for line in f:
1120
          fields = line.split()
1121
          if len(fields) > 1 and not fields[0].startswith('#'):
1122
            names = fields[1:]
1123
            if hostname in names:
1124
              while hostname in names:
1125
                names.remove(hostname)
1126
              if names:
1127
                out.write("%s %s\n" % (fields[0], ' '.join(names)))
1128
              continue
1129

    
1130
          out.write(line)
1131

    
1132
        out.flush()
1133
        os.fsync(out)
1134
        os.chmod(tmpname, 0644)
1135
        os.rename(tmpname, file_name)
1136
      finally:
1137
        f.close()
1138
    finally:
1139
      out.close()
1140
  except:
1141
    RemoveFile(tmpname)
1142
    raise
1143

    
1144

    
1145
def RemoveHostFromEtcHosts(hostname):
1146
  """Wrapper around RemoveEtcHostsEntry.
1147

1148
  @type hostname: str
1149
  @param hostname: hostname that will be resolved and its
1150
      full and shot name will be removed from
1151
      L{constants.ETC_HOSTS}
1152

1153
  """
1154
  hi = HostInfo(name=hostname)
1155
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1156
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1157

    
1158

    
1159
def TimestampForFilename():
1160
  """Returns the current time formatted for filenames.
1161

1162
  The format doesn't contain colons as some shells and applications them as
1163
  separators.
1164

1165
  """
1166
  return time.strftime("%Y-%m-%d_%H_%M_%S")
1167

    
1168

    
1169
def CreateBackup(file_name):
1170
  """Creates a backup of a file.
1171

1172
  @type file_name: str
1173
  @param file_name: file to be backed up
1174
  @rtype: str
1175
  @return: the path to the newly created backup
1176
  @raise errors.ProgrammerError: for invalid file names
1177

1178
  """
1179
  if not os.path.isfile(file_name):
1180
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
1181
                                file_name)
1182

    
1183
  prefix = ("%s.backup-%s." %
1184
            (os.path.basename(file_name), TimestampForFilename()))
1185
  dir_name = os.path.dirname(file_name)
1186

    
1187
  fsrc = open(file_name, 'rb')
1188
  try:
1189
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
1190
    fdst = os.fdopen(fd, 'wb')
1191
    try:
1192
      logging.debug("Backing up %s at %s", file_name, backup_name)
1193
      shutil.copyfileobj(fsrc, fdst)
1194
    finally:
1195
      fdst.close()
1196
  finally:
1197
    fsrc.close()
1198

    
1199
  return backup_name
1200

    
1201

    
1202
def ShellQuote(value):
1203
  """Quotes shell argument according to POSIX.
1204

1205
  @type value: str
1206
  @param value: the argument to be quoted
1207
  @rtype: str
1208
  @return: the quoted value
1209

1210
  """
1211
  if _re_shell_unquoted.match(value):
1212
    return value
1213
  else:
1214
    return "'%s'" % value.replace("'", "'\\''")
1215

    
1216

    
1217
def ShellQuoteArgs(args):
1218
  """Quotes a list of shell arguments.
1219

1220
  @type args: list
1221
  @param args: list of arguments to be quoted
1222
  @rtype: str
1223
  @return: the quoted arguments concatenated with spaces
1224

1225
  """
1226
  return ' '.join([ShellQuote(i) for i in args])
1227

    
1228

    
1229
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1230
  """Simple ping implementation using TCP connect(2).
1231

1232
  Check if the given IP is reachable by doing attempting a TCP connect
1233
  to it.
1234

1235
  @type target: str
1236
  @param target: the IP or hostname to ping
1237
  @type port: int
1238
  @param port: the port to connect to
1239
  @type timeout: int
1240
  @param timeout: the timeout on the connection attempt
1241
  @type live_port_needed: boolean
1242
  @param live_port_needed: whether a closed port will cause the
1243
      function to return failure, as if there was a timeout
1244
  @type source: str or None
1245
  @param source: if specified, will cause the connect to be made
1246
      from this specific source address; failures to bind other
1247
      than C{EADDRNOTAVAIL} will be ignored
1248

1249
  """
1250
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1251

    
1252
  success = False
1253

    
1254
  if source is not None:
1255
    try:
1256
      sock.bind((source, 0))
1257
    except socket.error, (errcode, _):
1258
      if errcode == errno.EADDRNOTAVAIL:
1259
        success = False
1260

    
1261
  sock.settimeout(timeout)
1262

    
1263
  try:
1264
    sock.connect((target, port))
1265
    sock.close()
1266
    success = True
1267
  except socket.timeout:
1268
    success = False
1269
  except socket.error, (errcode, _):
1270
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1271

    
1272
  return success
1273

    
1274

    
1275
def OwnIpAddress(address):
1276
  """Check if the current host has the the given IP address.
1277

1278
  Currently this is done by TCP-pinging the address from the loopback
1279
  address.
1280

1281
  @type address: string
1282
  @param address: the address to check
1283
  @rtype: bool
1284
  @return: True if we own the address
1285

1286
  """
1287
  return TcpPing(address, constants.DEFAULT_NODED_PORT,
1288
                 source=constants.LOCALHOST_IP_ADDRESS)
1289

    
1290

    
1291
def ListVisibleFiles(path):
1292
  """Returns a list of visible files in a directory.
1293

1294
  @type path: str
1295
  @param path: the directory to enumerate
1296
  @rtype: list
1297
  @return: the list of all files not starting with a dot
1298
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
1299

1300
  """
1301
  if not IsNormAbsPath(path):
1302
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
1303
                                 " absolute/normalized: '%s'" % path)
1304
  files = [i for i in os.listdir(path) if not i.startswith(".")]
1305
  files.sort()
1306
  return files
1307

    
1308

    
1309
def GetHomeDir(user, default=None):
1310
  """Try to get the homedir of the given user.
1311

1312
  The user can be passed either as a string (denoting the name) or as
1313
  an integer (denoting the user id). If the user is not found, the
1314
  'default' argument is returned, which defaults to None.
1315

1316
  """
1317
  try:
1318
    if isinstance(user, basestring):
1319
      result = pwd.getpwnam(user)
1320
    elif isinstance(user, (int, long)):
1321
      result = pwd.getpwuid(user)
1322
    else:
1323
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
1324
                                   type(user))
1325
  except KeyError:
1326
    return default
1327
  return result.pw_dir
1328

    
1329

    
1330
def NewUUID():
1331
  """Returns a random UUID.
1332

1333
  @note: This is a Linux-specific method as it uses the /proc
1334
      filesystem.
1335
  @rtype: str
1336

1337
  """
1338
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1339

    
1340

    
1341
def GenerateSecret(numbytes=20):
1342
  """Generates a random secret.
1343

1344
  This will generate a pseudo-random secret returning an hex string
1345
  (so that it can be used where an ASCII string is needed).
1346

1347
  @param numbytes: the number of bytes which will be represented by the returned
1348
      string (defaulting to 20, the length of a SHA1 hash)
1349
  @rtype: str
1350
  @return: an hex representation of the pseudo-random sequence
1351

1352
  """
1353
  return os.urandom(numbytes).encode('hex')
1354

    
1355

    
1356
def EnsureDirs(dirs):
1357
  """Make required directories, if they don't exist.
1358

1359
  @param dirs: list of tuples (dir_name, dir_mode)
1360
  @type dirs: list of (string, integer)
1361

1362
  """
1363
  for dir_name, dir_mode in dirs:
1364
    try:
1365
      os.mkdir(dir_name, dir_mode)
1366
    except EnvironmentError, err:
1367
      if err.errno != errno.EEXIST:
1368
        raise errors.GenericError("Cannot create needed directory"
1369
                                  " '%s': %s" % (dir_name, err))
1370
    if not os.path.isdir(dir_name):
1371
      raise errors.GenericError("%s is not a directory" % dir_name)
1372

    
1373

    
1374
def ReadFile(file_name, size=-1):
1375
  """Reads a file.
1376

1377
  @type size: int
1378
  @param size: Read at most size bytes (if negative, entire file)
1379
  @rtype: str
1380
  @return: the (possibly partial) content of the file
1381

1382
  """
1383
  f = open(file_name, "r")
1384
  try:
1385
    return f.read(size)
1386
  finally:
1387
    f.close()
1388

    
1389

    
1390
def WriteFile(file_name, fn=None, data=None,
1391
              mode=None, uid=-1, gid=-1,
1392
              atime=None, mtime=None, close=True,
1393
              dry_run=False, backup=False,
1394
              prewrite=None, postwrite=None):
1395
  """(Over)write a file atomically.
1396

1397
  The file_name and either fn (a function taking one argument, the
1398
  file descriptor, and which should write the data to it) or data (the
1399
  contents of the file) must be passed. The other arguments are
1400
  optional and allow setting the file mode, owner and group, and the
1401
  mtime/atime of the file.
1402

1403
  If the function doesn't raise an exception, it has succeeded and the
1404
  target file has the new contents. If the function has raised an
1405
  exception, an existing target file should be unmodified and the
1406
  temporary file should be removed.
1407

1408
  @type file_name: str
1409
  @param file_name: the target filename
1410
  @type fn: callable
1411
  @param fn: content writing function, called with
1412
      file descriptor as parameter
1413
  @type data: str
1414
  @param data: contents of the file
1415
  @type mode: int
1416
  @param mode: file mode
1417
  @type uid: int
1418
  @param uid: the owner of the file
1419
  @type gid: int
1420
  @param gid: the group of the file
1421
  @type atime: int
1422
  @param atime: a custom access time to be set on the file
1423
  @type mtime: int
1424
  @param mtime: a custom modification time to be set on the file
1425
  @type close: boolean
1426
  @param close: whether to close file after writing it
1427
  @type prewrite: callable
1428
  @param prewrite: function to be called before writing content
1429
  @type postwrite: callable
1430
  @param postwrite: function to be called after writing content
1431

1432
  @rtype: None or int
1433
  @return: None if the 'close' parameter evaluates to True,
1434
      otherwise the file descriptor
1435

1436
  @raise errors.ProgrammerError: if any of the arguments are not valid
1437

1438
  """
1439
  if not os.path.isabs(file_name):
1440
    raise errors.ProgrammerError("Path passed to WriteFile is not"
1441
                                 " absolute: '%s'" % file_name)
1442

    
1443
  if [fn, data].count(None) != 1:
1444
    raise errors.ProgrammerError("fn or data required")
1445

    
1446
  if [atime, mtime].count(None) == 1:
1447
    raise errors.ProgrammerError("Both atime and mtime must be either"
1448
                                 " set or None")
1449

    
1450
  if backup and not dry_run and os.path.isfile(file_name):
1451
    CreateBackup(file_name)
1452

    
1453
  dir_name, base_name = os.path.split(file_name)
1454
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
1455
  do_remove = True
1456
  # here we need to make sure we remove the temp file, if any error
1457
  # leaves it in place
1458
  try:
1459
    if uid != -1 or gid != -1:
1460
      os.chown(new_name, uid, gid)
1461
    if mode:
1462
      os.chmod(new_name, mode)
1463
    if callable(prewrite):
1464
      prewrite(fd)
1465
    if data is not None:
1466
      os.write(fd, data)
1467
    else:
1468
      fn(fd)
1469
    if callable(postwrite):
1470
      postwrite(fd)
1471
    os.fsync(fd)
1472
    if atime is not None and mtime is not None:
1473
      os.utime(new_name, (atime, mtime))
1474
    if not dry_run:
1475
      os.rename(new_name, file_name)
1476
      do_remove = False
1477
  finally:
1478
    if close:
1479
      os.close(fd)
1480
      result = None
1481
    else:
1482
      result = fd
1483
    if do_remove:
1484
      RemoveFile(new_name)
1485

    
1486
  return result
1487

    
1488

    
1489
def FirstFree(seq, base=0):
1490
  """Returns the first non-existing integer from seq.
1491

1492
  The seq argument should be a sorted list of positive integers. The
1493
  first time the index of an element is smaller than the element
1494
  value, the index will be returned.
1495

1496
  The base argument is used to start at a different offset,
1497
  i.e. C{[3, 4, 6]} with I{offset=3} will return 5.
1498

1499
  Example: C{[0, 1, 3]} will return I{2}.
1500

1501
  @type seq: sequence
1502
  @param seq: the sequence to be analyzed.
1503
  @type base: int
1504
  @param base: use this value as the base index of the sequence
1505
  @rtype: int
1506
  @return: the first non-used index in the sequence
1507

1508
  """
1509
  for idx, elem in enumerate(seq):
1510
    assert elem >= base, "Passed element is higher than base offset"
1511
    if elem > idx + base:
1512
      # idx is not used
1513
      return idx + base
1514
  return None
1515

    
1516

    
1517
def SingleWaitForFdCondition(fdobj, event, timeout):
1518
  """Waits for a condition to occur on the socket.
1519

1520
  Immediately returns at the first interruption.
1521

1522
  @type fdobj: integer or object supporting a fileno() method
1523
  @param fdobj: entity to wait for events on
1524
  @type event: integer
1525
  @param event: ORed condition (see select module)
1526
  @type timeout: float or None
1527
  @param timeout: Timeout in seconds
1528
  @rtype: int or None
1529
  @return: None for timeout, otherwise occured conditions
1530

1531
  """
1532
  check = (event | select.POLLPRI |
1533
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
1534

    
1535
  if timeout is not None:
1536
    # Poller object expects milliseconds
1537
    timeout *= 1000
1538

    
1539
  poller = select.poll()
1540
  poller.register(fdobj, event)
1541
  try:
1542
    # TODO: If the main thread receives a signal and we have no timeout, we
1543
    # could wait forever. This should check a global "quit" flag or something
1544
    # every so often.
1545
    io_events = poller.poll(timeout)
1546
  except select.error, err:
1547
    if err[0] != errno.EINTR:
1548
      raise
1549
    io_events = []
1550
  if io_events and io_events[0][1] & check:
1551
    return io_events[0][1]
1552
  else:
1553
    return None
1554

    
1555

    
1556
class FdConditionWaiterHelper(object):
1557
  """Retry helper for WaitForFdCondition.
1558

1559
  This class contains the retried and wait functions that make sure
1560
  WaitForFdCondition can continue waiting until the timeout is actually
1561
  expired.
1562

1563
  """
1564

    
1565
  def __init__(self, timeout):
1566
    self.timeout = timeout
1567

    
1568
  def Poll(self, fdobj, event):
1569
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
1570
    if result is None:
1571
      raise RetryAgain()
1572
    else:
1573
      return result
1574

    
1575
  def UpdateTimeout(self, timeout):
1576
    self.timeout = timeout
1577

    
1578

    
1579
def WaitForFdCondition(fdobj, event, timeout):
1580
  """Waits for a condition to occur on the socket.
1581

1582
  Retries until the timeout is expired, even if interrupted.
1583

1584
  @type fdobj: integer or object supporting a fileno() method
1585
  @param fdobj: entity to wait for events on
1586
  @type event: integer
1587
  @param event: ORed condition (see select module)
1588
  @type timeout: float or None
1589
  @param timeout: Timeout in seconds
1590
  @rtype: int or None
1591
  @return: None for timeout, otherwise occured conditions
1592

1593
  """
1594
  if timeout is not None:
1595
    retrywaiter = FdConditionWaiterHelper(timeout)
1596
    try:
1597
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
1598
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
1599
    except RetryTimeout:
1600
      result = None
1601
  else:
1602
    result = None
1603
    while result is None:
1604
      result = SingleWaitForFdCondition(fdobj, event, timeout)
1605
  return result
1606

    
1607

    
1608
def UniqueSequence(seq):
1609
  """Returns a list with unique elements.
1610

1611
  Element order is preserved.
1612

1613
  @type seq: sequence
1614
  @param seq: the sequence with the source elements
1615
  @rtype: list
1616
  @return: list of unique elements from seq
1617

1618
  """
1619
  seen = set()
1620
  return [i for i in seq if i not in seen and not seen.add(i)]
1621

    
1622

    
1623
def NormalizeAndValidateMac(mac):
1624
  """Normalizes and check if a MAC address is valid.
1625

1626
  Checks whether the supplied MAC address is formally correct, only
1627
  accepts colon separated format. Normalize it to all lower.
1628

1629
  @type mac: str
1630
  @param mac: the MAC to be validated
1631
  @rtype: str
1632
  @return: returns the normalized and validated MAC.
1633

1634
  @raise errors.OpPrereqError: If the MAC isn't valid
1635

1636
  """
1637
  mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$", re.I)
1638
  if not mac_check.match(mac):
1639
    raise errors.OpPrereqError("Invalid MAC address specified: %s" %
1640
                               mac, errors.ECODE_INVAL)
1641

    
1642
  return mac.lower()
1643

    
1644

    
1645
def TestDelay(duration):
1646
  """Sleep for a fixed amount of time.
1647

1648
  @type duration: float
1649
  @param duration: the sleep duration
1650
  @rtype: boolean
1651
  @return: False for negative value, True otherwise
1652

1653
  """
1654
  if duration < 0:
1655
    return False, "Invalid sleep duration"
1656
  time.sleep(duration)
1657
  return True, None
1658

    
1659

    
1660
def _CloseFDNoErr(fd, retries=5):
1661
  """Close a file descriptor ignoring errors.
1662

1663
  @type fd: int
1664
  @param fd: the file descriptor
1665
  @type retries: int
1666
  @param retries: how many retries to make, in case we get any
1667
      other error than EBADF
1668

1669
  """
1670
  try:
1671
    os.close(fd)
1672
  except OSError, err:
1673
    if err.errno != errno.EBADF:
1674
      if retries > 0:
1675
        _CloseFDNoErr(fd, retries - 1)
1676
    # else either it's closed already or we're out of retries, so we
1677
    # ignore this and go on
1678

    
1679

    
1680
def CloseFDs(noclose_fds=None):
1681
  """Close file descriptors.
1682

1683
  This closes all file descriptors above 2 (i.e. except
1684
  stdin/out/err).
1685

1686
  @type noclose_fds: list or None
1687
  @param noclose_fds: if given, it denotes a list of file descriptor
1688
      that should not be closed
1689

1690
  """
1691
  # Default maximum for the number of available file descriptors.
1692
  if 'SC_OPEN_MAX' in os.sysconf_names:
1693
    try:
1694
      MAXFD = os.sysconf('SC_OPEN_MAX')
1695
      if MAXFD < 0:
1696
        MAXFD = 1024
1697
    except OSError:
1698
      MAXFD = 1024
1699
  else:
1700
    MAXFD = 1024
1701
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1702
  if (maxfd == resource.RLIM_INFINITY):
1703
    maxfd = MAXFD
1704

    
1705
  # Iterate through and close all file descriptors (except the standard ones)
1706
  for fd in range(3, maxfd):
1707
    if noclose_fds and fd in noclose_fds:
1708
      continue
1709
    _CloseFDNoErr(fd)
1710

    
1711

    
1712
def Daemonize(logfile):
1713
  """Daemonize the current process.
1714

1715
  This detaches the current process from the controlling terminal and
1716
  runs it in the background as a daemon.
1717

1718
  @type logfile: str
1719
  @param logfile: the logfile to which we should redirect stdout/stderr
1720
  @rtype: int
1721
  @return: the value zero
1722

1723
  """
1724
  # pylint: disable-msg=W0212
1725
  # yes, we really want os._exit
1726
  UMASK = 077
1727
  WORKDIR = "/"
1728

    
1729
  # this might fail
1730
  pid = os.fork()
1731
  if (pid == 0):  # The first child.
1732
    os.setsid()
1733
    # this might fail
1734
    pid = os.fork() # Fork a second child.
1735
    if (pid == 0):  # The second child.
1736
      os.chdir(WORKDIR)
1737
      os.umask(UMASK)
1738
    else:
1739
      # exit() or _exit()?  See below.
1740
      os._exit(0) # Exit parent (the first child) of the second child.
1741
  else:
1742
    os._exit(0) # Exit parent of the first child.
1743

    
1744
  for fd in range(3):
1745
    _CloseFDNoErr(fd)
1746
  i = os.open("/dev/null", os.O_RDONLY) # stdin
1747
  assert i == 0, "Can't close/reopen stdin"
1748
  i = os.open(logfile, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600) # stdout
1749
  assert i == 1, "Can't close/reopen stdout"
1750
  # Duplicate standard output to standard error.
1751
  os.dup2(1, 2)
1752
  return 0
1753

    
1754

    
1755
def DaemonPidFileName(name):
1756
  """Compute a ganeti pid file absolute path
1757

1758
  @type name: str
1759
  @param name: the daemon name
1760
  @rtype: str
1761
  @return: the full path to the pidfile corresponding to the given
1762
      daemon name
1763

1764
  """
1765
  return PathJoin(constants.RUN_GANETI_DIR, "%s.pid" % name)
1766

    
1767

    
1768
def EnsureDaemon(name):
1769
  """Check for and start daemon if not alive.
1770

1771
  """
1772
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
1773
  if result.failed:
1774
    logging.error("Can't start daemon '%s', failure %s, output: %s",
1775
                  name, result.fail_reason, result.output)
1776
    return False
1777

    
1778
  return True
1779

    
1780

    
1781
def WritePidFile(name):
1782
  """Write the current process pidfile.
1783

1784
  The file will be written to L{constants.RUN_GANETI_DIR}I{/name.pid}
1785

1786
  @type name: str
1787
  @param name: the daemon name to use
1788
  @raise errors.GenericError: if the pid file already exists and
1789
      points to a live process
1790

1791
  """
1792
  pid = os.getpid()
1793
  pidfilename = DaemonPidFileName(name)
1794
  if IsProcessAlive(ReadPidFile(pidfilename)):
1795
    raise errors.GenericError("%s contains a live process" % pidfilename)
1796

    
1797
  WriteFile(pidfilename, data="%d\n" % pid)
1798

    
1799

    
1800
def RemovePidFile(name):
1801
  """Remove the current process pidfile.
1802

1803
  Any errors are ignored.
1804

1805
  @type name: str
1806
  @param name: the daemon name used to derive the pidfile name
1807

1808
  """
1809
  pidfilename = DaemonPidFileName(name)
1810
  # TODO: we could check here that the file contains our pid
1811
  try:
1812
    RemoveFile(pidfilename)
1813
  except: # pylint: disable-msg=W0702
1814
    pass
1815

    
1816

    
1817
def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
1818
                waitpid=False):
1819
  """Kill a process given by its pid.
1820

1821
  @type pid: int
1822
  @param pid: The PID to terminate.
1823
  @type signal_: int
1824
  @param signal_: The signal to send, by default SIGTERM
1825
  @type timeout: int
1826
  @param timeout: The timeout after which, if the process is still alive,
1827
                  a SIGKILL will be sent. If not positive, no such checking
1828
                  will be done
1829
  @type waitpid: boolean
1830
  @param waitpid: If true, we should waitpid on this process after
1831
      sending signals, since it's our own child and otherwise it
1832
      would remain as zombie
1833

1834
  """
1835
  def _helper(pid, signal_, wait):
1836
    """Simple helper to encapsulate the kill/waitpid sequence"""
1837
    os.kill(pid, signal_)
1838
    if wait:
1839
      try:
1840
        os.waitpid(pid, os.WNOHANG)
1841
      except OSError:
1842
        pass
1843

    
1844
  if pid <= 0:
1845
    # kill with pid=0 == suicide
1846
    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
1847

    
1848
  if not IsProcessAlive(pid):
1849
    return
1850

    
1851
  _helper(pid, signal_, waitpid)
1852

    
1853
  if timeout <= 0:
1854
    return
1855

    
1856
  def _CheckProcess():
1857
    if not IsProcessAlive(pid):
1858
      return
1859

    
1860
    try:
1861
      (result_pid, _) = os.waitpid(pid, os.WNOHANG)
1862
    except OSError:
1863
      raise RetryAgain()
1864

    
1865
    if result_pid > 0:
1866
      return
1867

    
1868
    raise RetryAgain()
1869

    
1870
  try:
1871
    # Wait up to $timeout seconds
1872
    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
1873
  except RetryTimeout:
1874
    pass
1875

    
1876
  if IsProcessAlive(pid):
1877
    # Kill process if it's still alive
1878
    _helper(pid, signal.SIGKILL, waitpid)
1879

    
1880

    
1881
def FindFile(name, search_path, test=os.path.exists):
1882
  """Look for a filesystem object in a given path.
1883

1884
  This is an abstract method to search for filesystem object (files,
1885
  dirs) under a given search path.
1886

1887
  @type name: str
1888
  @param name: the name to look for
1889
  @type search_path: str
1890
  @param search_path: location to start at
1891
  @type test: callable
1892
  @param test: a function taking one argument that should return True
1893
      if the a given object is valid; the default value is
1894
      os.path.exists, causing only existing files to be returned
1895
  @rtype: str or None
1896
  @return: full path to the object if found, None otherwise
1897

1898
  """
1899
  # validate the filename mask
1900
  if constants.EXT_PLUGIN_MASK.match(name) is None:
1901
    logging.critical("Invalid value passed for external script name: '%s'",
1902
                     name)
1903
    return None
1904

    
1905
  for dir_name in search_path:
1906
    # FIXME: investigate switch to PathJoin
1907
    item_name = os.path.sep.join([dir_name, name])
1908
    # check the user test and that we're indeed resolving to the given
1909
    # basename
1910
    if test(item_name) and os.path.basename(item_name) == name:
1911
      return item_name
1912
  return None
1913

    
1914

    
1915
def CheckVolumeGroupSize(vglist, vgname, minsize):
1916
  """Checks if the volume group list is valid.
1917

1918
  The function will check if a given volume group is in the list of
1919
  volume groups and has a minimum size.
1920

1921
  @type vglist: dict
1922
  @param vglist: dictionary of volume group names and their size
1923
  @type vgname: str
1924
  @param vgname: the volume group we should check
1925
  @type minsize: int
1926
  @param minsize: the minimum size we accept
1927
  @rtype: None or str
1928
  @return: None for success, otherwise the error message
1929

1930
  """
1931
  vgsize = vglist.get(vgname, None)
1932
  if vgsize is None:
1933
    return "volume group '%s' missing" % vgname
1934
  elif vgsize < minsize:
1935
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
1936
            (vgname, minsize, vgsize))
1937
  return None
1938

    
1939

    
1940
def SplitTime(value):
1941
  """Splits time as floating point number into a tuple.
1942

1943
  @param value: Time in seconds
1944
  @type value: int or float
1945
  @return: Tuple containing (seconds, microseconds)
1946

1947
  """
1948
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
1949

    
1950
  assert 0 <= seconds, \
1951
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1952
  assert 0 <= microseconds <= 999999, \
1953
    "Microseconds must be 0-999999, but are %s" % microseconds
1954

    
1955
  return (int(seconds), int(microseconds))
1956

    
1957

    
1958
def MergeTime(timetuple):
1959
  """Merges a tuple into time as a floating point number.
1960

1961
  @param timetuple: Time as tuple, (seconds, microseconds)
1962
  @type timetuple: tuple
1963
  @return: Time as a floating point number expressed in seconds
1964

1965
  """
1966
  (seconds, microseconds) = timetuple
1967

    
1968
  assert 0 <= seconds, \
1969
    "Seconds must be larger than or equal to 0, but are %s" % seconds
1970
  assert 0 <= microseconds <= 999999, \
1971
    "Microseconds must be 0-999999, but are %s" % microseconds
1972

    
1973
  return float(seconds) + (float(microseconds) * 0.000001)
1974

    
1975

    
1976
def GetDaemonPort(daemon_name):
1977
  """Get the daemon port for this cluster.
1978

1979
  Note that this routine does not read a ganeti-specific file, but
1980
  instead uses C{socket.getservbyname} to allow pre-customization of
1981
  this parameter outside of Ganeti.
1982

1983
  @type daemon_name: string
1984
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
1985
  @rtype: int
1986

1987
  """
1988
  if daemon_name not in constants.DAEMONS_PORTS:
1989
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
1990

    
1991
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
1992
  try:
1993
    port = socket.getservbyname(daemon_name, proto)
1994
  except socket.error:
1995
    port = default_port
1996

    
1997
  return port
1998

    
1999

    
2000
def SetupLogging(logfile, debug=0, stderr_logging=False, program="",
2001
                 multithreaded=False, syslog=constants.SYSLOG_USAGE):
2002
  """Configures the logging module.
2003

2004
  @type logfile: str
2005
  @param logfile: the filename to which we should log
2006
  @type debug: integer
2007
  @param debug: if greater than zero, enable debug messages, otherwise
2008
      only those at C{INFO} and above level
2009
  @type stderr_logging: boolean
2010
  @param stderr_logging: whether we should also log to the standard error
2011
  @type program: str
2012
  @param program: the name under which we should log messages
2013
  @type multithreaded: boolean
2014
  @param multithreaded: if True, will add the thread name to the log file
2015
  @type syslog: string
2016
  @param syslog: one of 'no', 'yes', 'only':
2017
      - if no, syslog is not used
2018
      - if yes, syslog is used (in addition to file-logging)
2019
      - if only, only syslog is used
2020
  @raise EnvironmentError: if we can't open the log file and
2021
      syslog/stderr logging is disabled
2022

2023
  """
2024
  fmt = "%(asctime)s: " + program + " pid=%(process)d"
2025
  sft = program + "[%(process)d]:"
2026
  if multithreaded:
2027
    fmt += "/%(threadName)s"
2028
    sft += " (%(threadName)s)"
2029
  if debug:
2030
    fmt += " %(module)s:%(lineno)s"
2031
    # no debug info for syslog loggers
2032
  fmt += " %(levelname)s %(message)s"
2033
  # yes, we do want the textual level, as remote syslog will probably
2034
  # lose the error level, and it's easier to grep for it
2035
  sft += " %(levelname)s %(message)s"
2036
  formatter = logging.Formatter(fmt)
2037
  sys_fmt = logging.Formatter(sft)
2038

    
2039
  root_logger = logging.getLogger("")
2040
  root_logger.setLevel(logging.NOTSET)
2041

    
2042
  # Remove all previously setup handlers
2043
  for handler in root_logger.handlers:
2044
    handler.close()
2045
    root_logger.removeHandler(handler)
2046

    
2047
  if stderr_logging:
2048
    stderr_handler = logging.StreamHandler()
2049
    stderr_handler.setFormatter(formatter)
2050
    if debug:
2051
      stderr_handler.setLevel(logging.NOTSET)
2052
    else:
2053
      stderr_handler.setLevel(logging.CRITICAL)
2054
    root_logger.addHandler(stderr_handler)
2055

    
2056
  if syslog in (constants.SYSLOG_YES, constants.SYSLOG_ONLY):
2057
    facility = logging.handlers.SysLogHandler.LOG_DAEMON
2058
    syslog_handler = logging.handlers.SysLogHandler(constants.SYSLOG_SOCKET,
2059
                                                    facility)
2060
    syslog_handler.setFormatter(sys_fmt)
2061
    # Never enable debug over syslog
2062
    syslog_handler.setLevel(logging.INFO)
2063
    root_logger.addHandler(syslog_handler)
2064

    
2065
  if syslog != constants.SYSLOG_ONLY:
2066
    # this can fail, if the logging directories are not setup or we have
2067
    # a permisssion problem; in this case, it's best to log but ignore
2068
    # the error if stderr_logging is True, and if false we re-raise the
2069
    # exception since otherwise we could run but without any logs at all
2070
    try:
2071
      logfile_handler = logging.FileHandler(logfile)
2072
      logfile_handler.setFormatter(formatter)
2073
      if debug:
2074
        logfile_handler.setLevel(logging.DEBUG)
2075
      else:
2076
        logfile_handler.setLevel(logging.INFO)
2077
      root_logger.addHandler(logfile_handler)
2078
    except EnvironmentError:
2079
      if stderr_logging or syslog == constants.SYSLOG_YES:
2080
        logging.exception("Failed to enable logging to file '%s'", logfile)
2081
      else:
2082
        # we need to re-raise the exception
2083
        raise
2084

    
2085

    
2086
def IsNormAbsPath(path):
2087
  """Check whether a path is absolute and also normalized
2088

2089
  This avoids things like /dir/../../other/path to be valid.
2090

2091
  """
2092
  return os.path.normpath(path) == path and os.path.isabs(path)
2093

    
2094

    
2095
def PathJoin(*args):
2096
  """Safe-join a list of path components.
2097

2098
  Requirements:
2099
      - the first argument must be an absolute path
2100
      - no component in the path must have backtracking (e.g. /../),
2101
        since we check for normalization at the end
2102

2103
  @param args: the path components to be joined
2104
  @raise ValueError: for invalid paths
2105

2106
  """
2107
  # ensure we're having at least one path passed in
2108
  assert args
2109
  # ensure the first component is an absolute and normalized path name
2110
  root = args[0]
2111
  if not IsNormAbsPath(root):
2112
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
2113
  result = os.path.join(*args)
2114
  # ensure that the whole path is normalized
2115
  if not IsNormAbsPath(result):
2116
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
2117
  # check that we're still under the original prefix
2118
  prefix = os.path.commonprefix([root, result])
2119
  if prefix != root:
2120
    raise ValueError("Error: path joining resulted in different prefix"
2121
                     " (%s != %s)" % (prefix, root))
2122
  return result
2123

    
2124

    
2125
def TailFile(fname, lines=20):
2126
  """Return the last lines from a file.
2127

2128
  @note: this function will only read and parse the last 4KB of
2129
      the file; if the lines are very long, it could be that less
2130
      than the requested number of lines are returned
2131

2132
  @param fname: the file name
2133
  @type lines: int
2134
  @param lines: the (maximum) number of lines to return
2135

2136
  """
2137
  fd = open(fname, "r")
2138
  try:
2139
    fd.seek(0, 2)
2140
    pos = fd.tell()
2141
    pos = max(0, pos-4096)
2142
    fd.seek(pos, 0)
2143
    raw_data = fd.read()
2144
  finally:
2145
    fd.close()
2146

    
2147
  rows = raw_data.splitlines()
2148
  return rows[-lines:]
2149

    
2150

    
2151
def _ParseAsn1Generalizedtime(value):
2152
  """Parses an ASN1 GENERALIZEDTIME timestamp as used by pyOpenSSL.
2153

2154
  @type value: string
2155
  @param value: ASN1 GENERALIZEDTIME timestamp
2156

2157
  """
2158
  m = re.match(r"^(\d+)([-+]\d\d)(\d\d)$", value)
2159
  if m:
2160
    # We have an offset
2161
    asn1time = m.group(1)
2162
    hours = int(m.group(2))
2163
    minutes = int(m.group(3))
2164
    utcoffset = (60 * hours) + minutes
2165
  else:
2166
    if not value.endswith("Z"):
2167
      raise ValueError("Missing timezone")
2168
    asn1time = value[:-1]
2169
    utcoffset = 0
2170

    
2171
  parsed = time.strptime(asn1time, "%Y%m%d%H%M%S")
2172

    
2173
  tt = datetime.datetime(*(parsed[:7])) - datetime.timedelta(minutes=utcoffset)
2174

    
2175
  return calendar.timegm(tt.utctimetuple())
2176

    
2177

    
2178
def GetX509CertValidity(cert):
2179
  """Returns the validity period of the certificate.
2180

2181
  @type cert: OpenSSL.crypto.X509
2182
  @param cert: X509 certificate object
2183

2184
  """
2185
  # The get_notBefore and get_notAfter functions are only supported in
2186
  # pyOpenSSL 0.7 and above.
2187
  try:
2188
    get_notbefore_fn = cert.get_notBefore
2189
  except AttributeError:
2190
    not_before = None
2191
  else:
2192
    not_before_asn1 = get_notbefore_fn()
2193

    
2194
    if not_before_asn1 is None:
2195
      not_before = None
2196
    else:
2197
      not_before = _ParseAsn1Generalizedtime(not_before_asn1)
2198

    
2199
  try:
2200
    get_notafter_fn = cert.get_notAfter
2201
  except AttributeError:
2202
    not_after = None
2203
  else:
2204
    not_after_asn1 = get_notafter_fn()
2205

    
2206
    if not_after_asn1 is None:
2207
      not_after = None
2208
    else:
2209
      not_after = _ParseAsn1Generalizedtime(not_after_asn1)
2210

    
2211
  return (not_before, not_after)
2212

    
2213

    
2214
def SafeEncode(text):
2215
  """Return a 'safe' version of a source string.
2216

2217
  This function mangles the input string and returns a version that
2218
  should be safe to display/encode as ASCII. To this end, we first
2219
  convert it to ASCII using the 'backslashreplace' encoding which
2220
  should get rid of any non-ASCII chars, and then we process it
2221
  through a loop copied from the string repr sources in the python; we
2222
  don't use string_escape anymore since that escape single quotes and
2223
  backslashes too, and that is too much; and that escaping is not
2224
  stable, i.e. string_escape(string_escape(x)) != string_escape(x).
2225

2226
  @type text: str or unicode
2227
  @param text: input data
2228
  @rtype: str
2229
  @return: a safe version of text
2230

2231
  """
2232
  if isinstance(text, unicode):
2233
    # only if unicode; if str already, we handle it below
2234
    text = text.encode('ascii', 'backslashreplace')
2235
  resu = ""
2236
  for char in text:
2237
    c = ord(char)
2238
    if char  == '\t':
2239
      resu += r'\t'
2240
    elif char == '\n':
2241
      resu += r'\n'
2242
    elif char == '\r':
2243
      resu += r'\'r'
2244
    elif c < 32 or c >= 127: # non-printable
2245
      resu += "\\x%02x" % (c & 0xff)
2246
    else:
2247
      resu += char
2248
  return resu
2249

    
2250

    
2251
def UnescapeAndSplit(text, sep=","):
2252
  """Split and unescape a string based on a given separator.
2253

2254
  This function splits a string based on a separator where the
2255
  separator itself can be escape in order to be an element of the
2256
  elements. The escaping rules are (assuming coma being the
2257
  separator):
2258
    - a plain , separates the elements
2259
    - a sequence \\\\, (double backslash plus comma) is handled as a
2260
      backslash plus a separator comma
2261
    - a sequence \, (backslash plus comma) is handled as a
2262
      non-separator comma
2263

2264
  @type text: string
2265
  @param text: the string to split
2266
  @type sep: string
2267
  @param text: the separator
2268
  @rtype: string
2269
  @return: a list of strings
2270

2271
  """
2272
  # we split the list by sep (with no escaping at this stage)
2273
  slist = text.split(sep)
2274
  # next, we revisit the elements and if any of them ended with an odd
2275
  # number of backslashes, then we join it with the next
2276
  rlist = []
2277
  while slist:
2278
    e1 = slist.pop(0)
2279
    if e1.endswith("\\"):
2280
      num_b = len(e1) - len(e1.rstrip("\\"))
2281
      if num_b % 2 == 1:
2282
        e2 = slist.pop(0)
2283
        # here the backslashes remain (all), and will be reduced in
2284
        # the next step
2285
        rlist.append(e1 + sep + e2)
2286
        continue
2287
    rlist.append(e1)
2288
  # finally, replace backslash-something with something
2289
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
2290
  return rlist
2291

    
2292

    
2293
def CommaJoin(names):
2294
  """Nicely join a set of identifiers.
2295

2296
  @param names: set, list or tuple
2297
  @return: a string with the formatted results
2298

2299
  """
2300
  return ", ".join([str(val) for val in names])
2301

    
2302

    
2303
def BytesToMebibyte(value):
2304
  """Converts bytes to mebibytes.
2305

2306
  @type value: int
2307
  @param value: Value in bytes
2308
  @rtype: int
2309
  @return: Value in mebibytes
2310

2311
  """
2312
  return int(round(value / (1024.0 * 1024.0), 0))
2313

    
2314

    
2315
def CalculateDirectorySize(path):
2316
  """Calculates the size of a directory recursively.
2317

2318
  @type path: string
2319
  @param path: Path to directory
2320
  @rtype: int
2321
  @return: Size in mebibytes
2322

2323
  """
2324
  size = 0
2325

    
2326
  for (curpath, _, files) in os.walk(path):
2327
    for filename in files:
2328
      st = os.lstat(PathJoin(curpath, filename))
2329
      size += st.st_size
2330

    
2331
  return BytesToMebibyte(size)
2332

    
2333

    
2334
def GetFilesystemStats(path):
2335
  """Returns the total and free space on a filesystem.
2336

2337
  @type path: string
2338
  @param path: Path on filesystem to be examined
2339
  @rtype: int
2340
  @return: tuple of (Total space, Free space) in mebibytes
2341

2342
  """
2343
  st = os.statvfs(path)
2344

    
2345
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
2346
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
2347
  return (tsize, fsize)
2348

    
2349

    
2350
def RunInSeparateProcess(fn, *args):
2351
  """Runs a function in a separate process.
2352

2353
  Note: Only boolean return values are supported.
2354

2355
  @type fn: callable
2356
  @param fn: Function to be called
2357
  @rtype: bool
2358
  @return: Function's result
2359

2360
  """
2361
  pid = os.fork()
2362
  if pid == 0:
2363
    # Child process
2364
    try:
2365
      # In case the function uses temporary files
2366
      ResetTempfileModule()
2367

    
2368
      # Call function
2369
      result = int(bool(fn(*args)))
2370
      assert result in (0, 1)
2371
    except: # pylint: disable-msg=W0702
2372
      logging.exception("Error while calling function in separate process")
2373
      # 0 and 1 are reserved for the return value
2374
      result = 33
2375

    
2376
    os._exit(result) # pylint: disable-msg=W0212
2377

    
2378
  # Parent process
2379

    
2380
  # Avoid zombies and check exit code
2381
  (_, status) = os.waitpid(pid, 0)
2382

    
2383
  if os.WIFSIGNALED(status):
2384
    exitcode = None
2385
    signum = os.WTERMSIG(status)
2386
  else:
2387
    exitcode = os.WEXITSTATUS(status)
2388
    signum = None
2389

    
2390
  if not (exitcode in (0, 1) and signum is None):
2391
    raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
2392
                              (exitcode, signum))
2393

    
2394
  return bool(exitcode)
2395

    
2396

    
2397
def LockedMethod(fn):
2398
  """Synchronized object access decorator.
2399

2400
  This decorator is intended to protect access to an object using the
2401
  object's own lock which is hardcoded to '_lock'.
2402

2403
  """
2404
  def _LockDebug(*args, **kwargs):
2405
    if debug_locks:
2406
      logging.debug(*args, **kwargs)
2407

    
2408
  def wrapper(self, *args, **kwargs):
2409
    # pylint: disable-msg=W0212
2410
    assert hasattr(self, '_lock')
2411
    lock = self._lock
2412
    _LockDebug("Waiting for %s", lock)
2413
    lock.acquire()
2414
    try:
2415
      _LockDebug("Acquired %s", lock)
2416
      result = fn(self, *args, **kwargs)
2417
    finally:
2418
      _LockDebug("Releasing %s", lock)
2419
      lock.release()
2420
      _LockDebug("Released %s", lock)
2421
    return result
2422
  return wrapper
2423

    
2424

    
2425
def LockFile(fd):
2426
  """Locks a file using POSIX locks.
2427

2428
  @type fd: int
2429
  @param fd: the file descriptor we need to lock
2430

2431
  """
2432
  try:
2433
    fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
2434
  except IOError, err:
2435
    if err.errno == errno.EAGAIN:
2436
      raise errors.LockError("File already locked")
2437
    raise
2438

    
2439

    
2440
def FormatTime(val):
2441
  """Formats a time value.
2442

2443
  @type val: float or None
2444
  @param val: the timestamp as returned by time.time()
2445
  @return: a string value or N/A if we don't have a valid timestamp
2446

2447
  """
2448
  if val is None or not isinstance(val, (int, float)):
2449
    return "N/A"
2450
  # these two codes works on Linux, but they are not guaranteed on all
2451
  # platforms
2452
  return time.strftime("%F %T", time.localtime(val))
2453

    
2454

    
2455
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
2456
  """Reads the watcher pause file.
2457

2458
  @type filename: string
2459
  @param filename: Path to watcher pause file
2460
  @type now: None, float or int
2461
  @param now: Current time as Unix timestamp
2462
  @type remove_after: int
2463
  @param remove_after: Remove watcher pause file after specified amount of
2464
    seconds past the pause end time
2465

2466
  """
2467
  if now is None:
2468
    now = time.time()
2469

    
2470
  try:
2471
    value = ReadFile(filename)
2472
  except IOError, err:
2473
    if err.errno != errno.ENOENT:
2474
      raise
2475
    value = None
2476

    
2477
  if value is not None:
2478
    try:
2479
      value = int(value)
2480
    except ValueError:
2481
      logging.warning(("Watcher pause file (%s) contains invalid value,"
2482
                       " removing it"), filename)
2483
      RemoveFile(filename)
2484
      value = None
2485

    
2486
    if value is not None:
2487
      # Remove file if it's outdated
2488
      if now > (value + remove_after):
2489
        RemoveFile(filename)
2490
        value = None
2491

    
2492
      elif now > value:
2493
        value = None
2494

    
2495
  return value
2496

    
2497

    
2498
class RetryTimeout(Exception):
2499
  """Retry loop timed out.
2500

2501
  """
2502
  def RaiseInner(self):
2503
    if self.args and isinstance(self.args[0], Exception):
2504
      raise self.args[0]
2505
    else:
2506
      raise RetryTimeout(*self.args)
2507

    
2508

    
2509
class RetryAgain(Exception):
2510
  """Retry again.
2511

2512
  """
2513

    
2514

    
2515
class _RetryDelayCalculator(object):
2516
  """Calculator for increasing delays.
2517

2518
  """
2519
  __slots__ = [
2520
    "_factor",
2521
    "_limit",
2522
    "_next",
2523
    "_start",
2524
    ]
2525

    
2526
  def __init__(self, start, factor, limit):
2527
    """Initializes this class.
2528

2529
    @type start: float
2530
    @param start: Initial delay
2531
    @type factor: float
2532
    @param factor: Factor for delay increase
2533
    @type limit: float or None
2534
    @param limit: Upper limit for delay or None for no limit
2535

2536
    """
2537
    assert start > 0.0
2538
    assert factor >= 1.0
2539
    assert limit is None or limit >= 0.0
2540

    
2541
    self._start = start
2542
    self._factor = factor
2543
    self._limit = limit
2544

    
2545
    self._next = start
2546

    
2547
  def __call__(self):
2548
    """Returns current delay and calculates the next one.
2549

2550
    """
2551
    current = self._next
2552

    
2553
    # Update for next run
2554
    if self._limit is None or self._next < self._limit:
2555
      self._next = min(self._limit, self._next * self._factor)
2556

    
2557
    return current
2558

    
2559

    
2560
#: Special delay to specify whole remaining timeout
2561
RETRY_REMAINING_TIME = object()
2562

    
2563

    
2564
def Retry(fn, delay, timeout, args=None, wait_fn=time.sleep,
2565
          _time_fn=time.time):
2566
  """Call a function repeatedly until it succeeds.
2567

2568
  The function C{fn} is called repeatedly until it doesn't throw L{RetryAgain}
2569
  anymore. Between calls a delay, specified by C{delay}, is inserted. After a
2570
  total of C{timeout} seconds, this function throws L{RetryTimeout}.
2571

2572
  C{delay} can be one of the following:
2573
    - callable returning the delay length as a float
2574
    - Tuple of (start, factor, limit)
2575
    - L{RETRY_REMAINING_TIME} to sleep until the timeout expires (this is
2576
      useful when overriding L{wait_fn} to wait for an external event)
2577
    - A static delay as a number (int or float)
2578

2579
  @type fn: callable
2580
  @param fn: Function to be called
2581
  @param delay: Either a callable (returning the delay), a tuple of (start,
2582
                factor, limit) (see L{_RetryDelayCalculator}),
2583
                L{RETRY_REMAINING_TIME} or a number (int or float)
2584
  @type timeout: float
2585
  @param timeout: Total timeout
2586
  @type wait_fn: callable
2587
  @param wait_fn: Waiting function
2588
  @return: Return value of function
2589

2590
  """
2591
  assert callable(fn)
2592
  assert callable(wait_fn)
2593
  assert callable(_time_fn)
2594

    
2595
  if args is None:
2596
    args = []
2597

    
2598
  end_time = _time_fn() + timeout
2599

    
2600
  if callable(delay):
2601
    # External function to calculate delay
2602
    calc_delay = delay
2603

    
2604
  elif isinstance(delay, (tuple, list)):
2605
    # Increasing delay with optional upper boundary
2606
    (start, factor, limit) = delay
2607
    calc_delay = _RetryDelayCalculator(start, factor, limit)
2608

    
2609
  elif delay is RETRY_REMAINING_TIME:
2610
    # Always use the remaining time
2611
    calc_delay = None
2612

    
2613
  else:
2614
    # Static delay
2615
    calc_delay = lambda: delay
2616

    
2617
  assert calc_delay is None or callable(calc_delay)
2618

    
2619
  while True:
2620
    retry_args = []
2621
    try:
2622
      # pylint: disable-msg=W0142
2623
      return fn(*args)
2624
    except RetryAgain, err:
2625
      retry_args = err.args
2626
    except RetryTimeout:
2627
      raise errors.ProgrammerError("Nested retry loop detected that didn't"
2628
                                   " handle RetryTimeout")
2629

    
2630
    remaining_time = end_time - _time_fn()
2631

    
2632
    if remaining_time < 0.0:
2633
      # pylint: disable-msg=W0142
2634
      raise RetryTimeout(*retry_args)
2635

    
2636
    assert remaining_time >= 0.0
2637

    
2638
    if calc_delay is None:
2639
      wait_fn(remaining_time)
2640
    else:
2641
      current_delay = calc_delay()
2642
      if current_delay > 0.0:
2643
        wait_fn(current_delay)
2644

    
2645

    
2646
class FileLock(object):
2647
  """Utility class for file locks.
2648

2649
  """
2650
  def __init__(self, fd, filename):
2651
    """Constructor for FileLock.
2652

2653
    @type fd: file
2654
    @param fd: File object
2655
    @type filename: str
2656
    @param filename: Path of the file opened at I{fd}
2657

2658
    """
2659
    self.fd = fd
2660
    self.filename = filename
2661

    
2662
  @classmethod
2663
  def Open(cls, filename):
2664
    """Creates and opens a file to be used as a file-based lock.
2665

2666
    @type filename: string
2667
    @param filename: path to the file to be locked
2668

2669
    """
2670
    # Using "os.open" is necessary to allow both opening existing file
2671
    # read/write and creating if not existing. Vanilla "open" will truncate an
2672
    # existing file -or- allow creating if not existing.
2673
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2674
               filename)
2675

    
2676
  def __del__(self):
2677
    self.Close()
2678

    
2679
  def Close(self):
2680
    """Close the file and release the lock.
2681

2682
    """
2683
    if hasattr(self, "fd") and self.fd:
2684
      self.fd.close()
2685
      self.fd = None
2686

    
2687
  def _flock(self, flag, blocking, timeout, errmsg):
2688
    """Wrapper for fcntl.flock.
2689

2690
    @type flag: int
2691
    @param flag: operation flag
2692
    @type blocking: bool
2693
    @param blocking: whether the operation should be done in blocking mode.
2694
    @type timeout: None or float
2695
    @param timeout: for how long the operation should be retried (implies
2696
                    non-blocking mode).
2697
    @type errmsg: string
2698
    @param errmsg: error message in case operation fails.
2699

2700
    """
2701
    assert self.fd, "Lock was closed"
2702
    assert timeout is None or timeout >= 0, \
2703
      "If specified, timeout must be positive"
2704
    assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
2705

    
2706
    # When a timeout is used, LOCK_NB must always be set
2707
    if not (timeout is None and blocking):
2708
      flag |= fcntl.LOCK_NB
2709

    
2710
    if timeout is None:
2711
      self._Lock(self.fd, flag, timeout)
2712
    else:
2713
      try:
2714
        Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
2715
              args=(self.fd, flag, timeout))
2716
      except RetryTimeout:
2717
        raise errors.LockError(errmsg)
2718

    
2719
  @staticmethod
2720
  def _Lock(fd, flag, timeout):
2721
    try:
2722
      fcntl.flock(fd, flag)
2723
    except IOError, err:
2724
      if timeout is not None and err.errno == errno.EAGAIN:
2725
        raise RetryAgain()
2726

    
2727
      logging.exception("fcntl.flock failed")
2728
      raise
2729

    
2730
  def Exclusive(self, blocking=False, timeout=None):
2731
    """Locks the file in exclusive mode.
2732

2733
    @type blocking: boolean
2734
    @param blocking: whether to block and wait until we
2735
        can lock the file or return immediately
2736
    @type timeout: int or None
2737
    @param timeout: if not None, the duration to wait for the lock
2738
        (in blocking mode)
2739

2740
    """
2741
    self._flock(fcntl.LOCK_EX, blocking, timeout,
2742
                "Failed to lock %s in exclusive mode" % self.filename)
2743

    
2744
  def Shared(self, blocking=False, timeout=None):
2745
    """Locks the file in shared mode.
2746

2747
    @type blocking: boolean
2748
    @param blocking: whether to block and wait until we
2749
        can lock the file or return immediately
2750
    @type timeout: int or None
2751
    @param timeout: if not None, the duration to wait for the lock
2752
        (in blocking mode)
2753

2754
    """
2755
    self._flock(fcntl.LOCK_SH, blocking, timeout,
2756
                "Failed to lock %s in shared mode" % self.filename)
2757

    
2758
  def Unlock(self, blocking=True, timeout=None):
2759
    """Unlocks the file.
2760

2761
    According to C{flock(2)}, unlocking can also be a nonblocking
2762
    operation::
2763

2764
      To make a non-blocking request, include LOCK_NB with any of the above
2765
      operations.
2766

2767
    @type blocking: boolean
2768
    @param blocking: whether to block and wait until we
2769
        can lock the file or return immediately
2770
    @type timeout: int or None
2771
    @param timeout: if not None, the duration to wait for the lock
2772
        (in blocking mode)
2773

2774
    """
2775
    self._flock(fcntl.LOCK_UN, blocking, timeout,
2776
                "Failed to unlock %s" % self.filename)
2777

    
2778

    
2779
class LineSplitter:
2780
  """Splits data chunks into lines separated by newline.
2781

2782
  Instances provide a file-like interface.
2783

2784
  """
2785
  def __init__(self, line_fn, *args):
2786
    """Initializes this class.
2787

2788
    @type line_fn: callable
2789
    @param line_fn: Function called for each line, first parameter is line
2790
    @param args: Extra arguments for L{line_fn}
2791

2792
    """
2793
    assert callable(line_fn)
2794

    
2795
    if args:
2796
      # Python 2.4 doesn't have functools.partial yet
2797
      self._line_fn = \
2798
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
2799
    else:
2800
      self._line_fn = line_fn
2801

    
2802
    self._lines = collections.deque()
2803
    self._buffer = ""
2804

    
2805
  def write(self, data):
2806
    parts = (self._buffer + data).split("\n")
2807
    self._buffer = parts.pop()
2808
    self._lines.extend(parts)
2809

    
2810
  def flush(self):
2811
    while self._lines:
2812
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
2813

    
2814
  def close(self):
2815
    self.flush()
2816
    if self._buffer:
2817
      self._line_fn(self._buffer)
2818

    
2819

    
2820
def SignalHandled(signums):
2821
  """Signal Handled decoration.
2822

2823
  This special decorator installs a signal handler and then calls the target
2824
  function. The function must accept a 'signal_handlers' keyword argument,
2825
  which will contain a dict indexed by signal number, with SignalHandler
2826
  objects as values.
2827

2828
  The decorator can be safely stacked with iself, to handle multiple signals
2829
  with different handlers.
2830

2831
  @type signums: list
2832
  @param signums: signals to intercept
2833

2834
  """
2835
  def wrap(fn):
2836
    def sig_function(*args, **kwargs):
2837
      assert 'signal_handlers' not in kwargs or \
2838
             kwargs['signal_handlers'] is None or \
2839
             isinstance(kwargs['signal_handlers'], dict), \
2840
             "Wrong signal_handlers parameter in original function call"
2841
      if 'signal_handlers' in kwargs and kwargs['signal_handlers'] is not None:
2842
        signal_handlers = kwargs['signal_handlers']
2843
      else:
2844
        signal_handlers = {}
2845
        kwargs['signal_handlers'] = signal_handlers
2846
      sighandler = SignalHandler(signums)
2847
      try:
2848
        for sig in signums:
2849
          signal_handlers[sig] = sighandler
2850
        return fn(*args, **kwargs)
2851
      finally:
2852
        sighandler.Reset()
2853
    return sig_function
2854
  return wrap
2855

    
2856

    
2857
class SignalHandler(object):
2858
  """Generic signal handler class.
2859

2860
  It automatically restores the original handler when deconstructed or
2861
  when L{Reset} is called. You can either pass your own handler
2862
  function in or query the L{called} attribute to detect whether the
2863
  signal was sent.
2864

2865
  @type signum: list
2866
  @ivar signum: the signals we handle
2867
  @type called: boolean
2868
  @ivar called: tracks whether any of the signals have been raised
2869

2870
  """
2871
  def __init__(self, signum):
2872
    """Constructs a new SignalHandler instance.
2873

2874
    @type signum: int or list of ints
2875
    @param signum: Single signal number or set of signal numbers
2876

2877
    """
2878
    self.signum = set(signum)
2879
    self.called = False
2880

    
2881
    self._previous = {}
2882
    try:
2883
      for signum in self.signum:
2884
        # Setup handler
2885
        prev_handler = signal.signal(signum, self._HandleSignal)
2886
        try:
2887
          self._previous[signum] = prev_handler
2888
        except:
2889
          # Restore previous handler
2890
          signal.signal(signum, prev_handler)
2891
          raise
2892
    except:
2893
      # Reset all handlers
2894
      self.Reset()
2895
      # Here we have a race condition: a handler may have already been called,
2896
      # but there's not much we can do about it at this point.
2897
      raise
2898

    
2899
  def __del__(self):
2900
    self.Reset()
2901

    
2902
  def Reset(self):
2903
    """Restore previous handler.
2904

2905
    This will reset all the signals to their previous handlers.
2906

2907
    """
2908
    for signum, prev_handler in self._previous.items():
2909
      signal.signal(signum, prev_handler)
2910
      # If successful, remove from dict
2911
      del self._previous[signum]
2912

    
2913
  def Clear(self):
2914
    """Unsets the L{called} flag.
2915

2916
    This function can be used in case a signal may arrive several times.
2917

2918
    """
2919
    self.called = False
2920

    
2921
  # we don't care about arguments, but we leave them named for the future
2922
  def _HandleSignal(self, signum, frame): # pylint: disable-msg=W0613
2923
    """Actual signal handling function.
2924

2925
    """
2926
    # This is not nice and not absolutely atomic, but it appears to be the only
2927
    # solution in Python -- there are no atomic types.
2928
    self.called = True
2929

    
2930

    
2931
class FieldSet(object):
2932
  """A simple field set.
2933

2934
  Among the features are:
2935
    - checking if a string is among a list of static string or regex objects
2936
    - checking if a whole list of string matches
2937
    - returning the matching groups from a regex match
2938

2939
  Internally, all fields are held as regular expression objects.
2940

2941
  """
2942
  def __init__(self, *items):
2943
    self.items = [re.compile("^%s$" % value) for value in items]
2944

    
2945
  def Extend(self, other_set):
2946
    """Extend the field set with the items from another one"""
2947
    self.items.extend(other_set.items)
2948

    
2949
  def Matches(self, field):
2950
    """Checks if a field matches the current set
2951

2952
    @type field: str
2953
    @param field: the string to match
2954
    @return: either None or a regular expression match object
2955

2956
    """
2957
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
2958
      return m
2959
    return None
2960

    
2961
  def NonMatching(self, items):
2962
    """Returns the list of fields not matching the current set
2963

2964
    @type items: list
2965
    @param items: the list of fields to check
2966
    @rtype: list
2967
    @return: list of non-matching fields
2968

2969
    """
2970
    return [val for val in items if not self.Matches(val)]