Statistics
| Branch: | Tag: | Revision:

root / lib / utils / __init__.py @ 8dc76d54

History | View | Annotate | Download (22 kB)

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

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

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 b459a848 Andrea Spadaccini
# Allow wildcard import in pylint: disable=W0401
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
import os
32 a8083063 Iustin Pop
import re
33 4ca1b175 Alexander Schreiber
import errno
34 2f8b60b3 Iustin Pop
import pwd
35 f8326fca Andrea Spadaccini
import time
36 78feb6fb Guido Trotter
import itertools
37 9c233417 Iustin Pop
import select
38 bb698c1f Iustin Pop
import logging
39 de499029 Michael Hanselmann
import signal
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
from ganeti import errors
42 3aecd2c7 Iustin Pop
from ganeti import constants
43 716a32cb Guido Trotter
from ganeti import compat
44 a8083063 Iustin Pop
45 63fc4229 Michael Hanselmann
from ganeti.utils.algo import *
46 63fc4229 Michael Hanselmann
from ganeti.utils.filelock import *
47 63fc4229 Michael Hanselmann
from ganeti.utils.hash import *
48 63fc4229 Michael Hanselmann
from ganeti.utils.io import *
49 63fc4229 Michael Hanselmann
from ganeti.utils.log import *
50 8dc76d54 Guido Trotter
from ganeti.utils.mlock import *
51 63fc4229 Michael Hanselmann
from ganeti.utils.nodesetup import *
52 63fc4229 Michael Hanselmann
from ganeti.utils.process import *
53 63fc4229 Michael Hanselmann
from ganeti.utils.retry import *
54 63fc4229 Michael Hanselmann
from ganeti.utils.text import *
55 63fc4229 Michael Hanselmann
from ganeti.utils.wrapper import *
56 63fc4229 Michael Hanselmann
from ganeti.utils.x509 import *
57 16abfbc2 Alexander Schreiber
58 58885d79 Iustin Pop
59 28f34048 Michael Hanselmann
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
60 28f34048 Michael Hanselmann
61 d0c8c01d Iustin Pop
UUID_RE = re.compile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-"
62 d0c8c01d Iustin Pop
                     "[a-f0-9]{4}-[a-f0-9]{12}$")
63 05636402 Guido Trotter
64 7c0d6283 Michael Hanselmann
65 a5728081 Guido Trotter
def ForceDictType(target, key_types, allowed_values=None):
66 a5728081 Guido Trotter
  """Force the values of a dict to have certain types.
67 a5728081 Guido Trotter

68 a5728081 Guido Trotter
  @type target: dict
69 a5728081 Guido Trotter
  @param target: the dict to update
70 a5728081 Guido Trotter
  @type key_types: dict
71 a5728081 Guido Trotter
  @param key_types: dict mapping target dict keys to types
72 a5728081 Guido Trotter
                    in constants.ENFORCEABLE_TYPES
73 a5728081 Guido Trotter
  @type allowed_values: list
74 a5728081 Guido Trotter
  @keyword allowed_values: list of specially allowed values
75 a5728081 Guido Trotter

76 a5728081 Guido Trotter
  """
77 a5728081 Guido Trotter
  if allowed_values is None:
78 a5728081 Guido Trotter
    allowed_values = []
79 a5728081 Guido Trotter
80 8b46606c Guido Trotter
  if not isinstance(target, dict):
81 8b46606c Guido Trotter
    msg = "Expected dictionary, got '%s'" % target
82 8b46606c Guido Trotter
    raise errors.TypeEnforcementError(msg)
83 8b46606c Guido Trotter
84 a5728081 Guido Trotter
  for key in target:
85 a5728081 Guido Trotter
    if key not in key_types:
86 58a59652 Iustin Pop
      msg = "Unknown parameter '%s'" % key
87 a5728081 Guido Trotter
      raise errors.TypeEnforcementError(msg)
88 a5728081 Guido Trotter
89 a5728081 Guido Trotter
    if target[key] in allowed_values:
90 a5728081 Guido Trotter
      continue
91 a5728081 Guido Trotter
92 29921401 Iustin Pop
    ktype = key_types[key]
93 29921401 Iustin Pop
    if ktype not in constants.ENFORCEABLE_TYPES:
94 29921401 Iustin Pop
      msg = "'%s' has non-enforceable type %s" % (key, ktype)
95 a5728081 Guido Trotter
      raise errors.ProgrammerError(msg)
96 a5728081 Guido Trotter
97 59525e1f Michael Hanselmann
    if ktype in (constants.VTYPE_STRING, constants.VTYPE_MAYBE_STRING):
98 59525e1f Michael Hanselmann
      if target[key] is None and ktype == constants.VTYPE_MAYBE_STRING:
99 59525e1f Michael Hanselmann
        pass
100 59525e1f Michael Hanselmann
      elif not isinstance(target[key], basestring):
101 a5728081 Guido Trotter
        if isinstance(target[key], bool) and not target[key]:
102 d0c8c01d Iustin Pop
          target[key] = ""
103 a5728081 Guido Trotter
        else:
104 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid string" % (key, target[key])
105 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
106 29921401 Iustin Pop
    elif ktype == constants.VTYPE_BOOL:
107 a5728081 Guido Trotter
      if isinstance(target[key], basestring) and target[key]:
108 a5728081 Guido Trotter
        if target[key].lower() == constants.VALUE_FALSE:
109 a5728081 Guido Trotter
          target[key] = False
110 a5728081 Guido Trotter
        elif target[key].lower() == constants.VALUE_TRUE:
111 a5728081 Guido Trotter
          target[key] = True
112 a5728081 Guido Trotter
        else:
113 a5728081 Guido Trotter
          msg = "'%s' (value %s) is not a valid boolean" % (key, target[key])
114 a5728081 Guido Trotter
          raise errors.TypeEnforcementError(msg)
115 a5728081 Guido Trotter
      elif target[key]:
116 a5728081 Guido Trotter
        target[key] = True
117 a5728081 Guido Trotter
      else:
118 a5728081 Guido Trotter
        target[key] = False
119 29921401 Iustin Pop
    elif ktype == constants.VTYPE_SIZE:
120 a5728081 Guido Trotter
      try:
121 a5728081 Guido Trotter
        target[key] = ParseUnit(target[key])
122 a5728081 Guido Trotter
      except errors.UnitParseError, err:
123 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid size. error: %s" % \
124 a5728081 Guido Trotter
              (key, target[key], err)
125 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
126 29921401 Iustin Pop
    elif ktype == constants.VTYPE_INT:
127 a5728081 Guido Trotter
      try:
128 a5728081 Guido Trotter
        target[key] = int(target[key])
129 a5728081 Guido Trotter
      except (ValueError, TypeError):
130 a5728081 Guido Trotter
        msg = "'%s' (value %s) is not a valid integer" % (key, target[key])
131 a5728081 Guido Trotter
        raise errors.TypeEnforcementError(msg)
132 a5728081 Guido Trotter
133 a5728081 Guido Trotter
134 28f34048 Michael Hanselmann
def ValidateServiceName(name):
135 28f34048 Michael Hanselmann
  """Validate the given service name.
136 28f34048 Michael Hanselmann

137 28f34048 Michael Hanselmann
  @type name: number or string
138 28f34048 Michael Hanselmann
  @param name: Service name or port specification
139 28f34048 Michael Hanselmann

140 28f34048 Michael Hanselmann
  """
141 28f34048 Michael Hanselmann
  try:
142 28f34048 Michael Hanselmann
    numport = int(name)
143 28f34048 Michael Hanselmann
  except (ValueError, TypeError):
144 28f34048 Michael Hanselmann
    # Non-numeric service name
145 28f34048 Michael Hanselmann
    valid = _VALID_SERVICE_NAME_RE.match(name)
146 28f34048 Michael Hanselmann
  else:
147 28f34048 Michael Hanselmann
    # Numeric port (protocols other than TCP or UDP might need adjustments
148 28f34048 Michael Hanselmann
    # here)
149 28f34048 Michael Hanselmann
    valid = (numport >= 0 and numport < (1 << 16))
150 28f34048 Michael Hanselmann
151 28f34048 Michael Hanselmann
  if not valid:
152 28f34048 Michael Hanselmann
    raise errors.OpPrereqError("Invalid service name '%s'" % name,
153 28f34048 Michael Hanselmann
                               errors.ECODE_INVAL)
154 28f34048 Michael Hanselmann
155 28f34048 Michael Hanselmann
  return name
156 28f34048 Michael Hanselmann
157 28f34048 Michael Hanselmann
158 a8083063 Iustin Pop
def ListVolumeGroups():
159 a8083063 Iustin Pop
  """List volume groups and their size
160 a8083063 Iustin Pop

161 58885d79 Iustin Pop
  @rtype: dict
162 58885d79 Iustin Pop
  @return:
163 58885d79 Iustin Pop
       Dictionary with keys volume name and values
164 58885d79 Iustin Pop
       the size of the volume
165 a8083063 Iustin Pop

166 a8083063 Iustin Pop
  """
167 a8083063 Iustin Pop
  command = "vgs --noheadings --units m --nosuffix -o name,size"
168 a8083063 Iustin Pop
  result = RunCmd(command)
169 a8083063 Iustin Pop
  retval = {}
170 a8083063 Iustin Pop
  if result.failed:
171 a8083063 Iustin Pop
    return retval
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
  for line in result.stdout.splitlines():
174 a8083063 Iustin Pop
    try:
175 a8083063 Iustin Pop
      name, size = line.split()
176 a8083063 Iustin Pop
      size = int(float(size))
177 a8083063 Iustin Pop
    except (IndexError, ValueError), err:
178 bb698c1f Iustin Pop
      logging.error("Invalid output from vgs (%s): %s", err, line)
179 a8083063 Iustin Pop
      continue
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
    retval[name] = size
182 a8083063 Iustin Pop
183 a8083063 Iustin Pop
  return retval
184 a8083063 Iustin Pop
185 a8083063 Iustin Pop
186 a8083063 Iustin Pop
def BridgeExists(bridge):
187 a8083063 Iustin Pop
  """Check whether the given bridge exists in the system
188 a8083063 Iustin Pop

189 58885d79 Iustin Pop
  @type bridge: str
190 58885d79 Iustin Pop
  @param bridge: the bridge name to check
191 58885d79 Iustin Pop
  @rtype: boolean
192 58885d79 Iustin Pop
  @return: True if it does
193 a8083063 Iustin Pop

194 a8083063 Iustin Pop
  """
195 a8083063 Iustin Pop
  return os.path.isdir("/sys/class/net/%s/bridge" % bridge)
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
def TryConvert(fn, val):
199 a8083063 Iustin Pop
  """Try to convert a value ignoring errors.
200 a8083063 Iustin Pop

201 58885d79 Iustin Pop
  This function tries to apply function I{fn} to I{val}. If no
202 58885d79 Iustin Pop
  C{ValueError} or C{TypeError} exceptions are raised, it will return
203 58885d79 Iustin Pop
  the result, else it will return the original value. Any other
204 58885d79 Iustin Pop
  exceptions are propagated to the caller.
205 58885d79 Iustin Pop

206 58885d79 Iustin Pop
  @type fn: callable
207 58885d79 Iustin Pop
  @param fn: function to apply to the value
208 58885d79 Iustin Pop
  @param val: the value to be converted
209 58885d79 Iustin Pop
  @return: The converted value if the conversion was successful,
210 58885d79 Iustin Pop
      otherwise the original value.
211 a8083063 Iustin Pop

212 a8083063 Iustin Pop
  """
213 a8083063 Iustin Pop
  try:
214 a8083063 Iustin Pop
    nv = fn(val)
215 7c4d6c7b Michael Hanselmann
  except (ValueError, TypeError):
216 a8083063 Iustin Pop
    nv = val
217 a8083063 Iustin Pop
  return nv
218 a8083063 Iustin Pop
219 a8083063 Iustin Pop
220 31155d60 Balazs Lecz
def ParseCpuMask(cpu_mask):
221 31155d60 Balazs Lecz
  """Parse a CPU mask definition and return the list of CPU IDs.
222 31155d60 Balazs Lecz

223 31155d60 Balazs Lecz
  CPU mask format: comma-separated list of CPU IDs
224 31155d60 Balazs Lecz
  or dash-separated ID ranges
225 31155d60 Balazs Lecz
  Example: "0-2,5" -> "0,1,2,5"
226 31155d60 Balazs Lecz

227 31155d60 Balazs Lecz
  @type cpu_mask: str
228 31155d60 Balazs Lecz
  @param cpu_mask: CPU mask definition
229 31155d60 Balazs Lecz
  @rtype: list of int
230 31155d60 Balazs Lecz
  @return: list of CPU IDs
231 31155d60 Balazs Lecz

232 31155d60 Balazs Lecz
  """
233 31155d60 Balazs Lecz
  if not cpu_mask:
234 31155d60 Balazs Lecz
    return []
235 31155d60 Balazs Lecz
  cpu_list = []
236 31155d60 Balazs Lecz
  for range_def in cpu_mask.split(","):
237 31155d60 Balazs Lecz
    boundaries = range_def.split("-")
238 31155d60 Balazs Lecz
    n_elements = len(boundaries)
239 31155d60 Balazs Lecz
    if n_elements > 2:
240 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
241 31155d60 Balazs Lecz
                              " (only one hyphen allowed): %s" % range_def)
242 31155d60 Balazs Lecz
    try:
243 31155d60 Balazs Lecz
      lower = int(boundaries[0])
244 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
245 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for lower boundary of"
246 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
247 31155d60 Balazs Lecz
    try:
248 31155d60 Balazs Lecz
      higher = int(boundaries[-1])
249 31155d60 Balazs Lecz
    except (ValueError, TypeError), err:
250 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID value for higher boundary of"
251 31155d60 Balazs Lecz
                              " CPU ID range: %s" % str(err))
252 31155d60 Balazs Lecz
    if lower > higher:
253 31155d60 Balazs Lecz
      raise errors.ParseError("Invalid CPU ID range definition"
254 31155d60 Balazs Lecz
                              " (%d > %d): %s" % (lower, higher, range_def))
255 31155d60 Balazs Lecz
    cpu_list.extend(range(lower, higher + 1))
256 31155d60 Balazs Lecz
  return cpu_list
257 31155d60 Balazs Lecz
258 31155d60 Balazs Lecz
259 538ca33a Tsachy Shacham
def ParseMultiCpuMask(cpu_mask):
260 538ca33a Tsachy Shacham
  """Parse a multiple CPU mask definition and return the list of CPU IDs.
261 538ca33a Tsachy Shacham

262 538ca33a Tsachy Shacham
  CPU mask format: colon-separated list of comma-separated list of CPU IDs
263 538ca33a Tsachy Shacham
  or dash-separated ID ranges, with optional "all" as CPU value
264 538ca33a Tsachy Shacham
  Example: "0-2,5:all:1,5,6:2" -> [ [ 0,1,2,5 ], [ -1 ], [ 1, 5, 6 ], [ 2 ] ]
265 538ca33a Tsachy Shacham

266 538ca33a Tsachy Shacham
  @type cpu_mask: str
267 538ca33a Tsachy Shacham
  @param cpu_mask: multiple CPU mask definition
268 538ca33a Tsachy Shacham
  @rtype: list of lists of int
269 538ca33a Tsachy Shacham
  @return: list of lists of CPU IDs
270 538ca33a Tsachy Shacham

271 538ca33a Tsachy Shacham
  """
272 538ca33a Tsachy Shacham
  if not cpu_mask:
273 538ca33a Tsachy Shacham
    return []
274 538ca33a Tsachy Shacham
  cpu_list = []
275 538ca33a Tsachy Shacham
  for range_def in cpu_mask.split(constants.CPU_PINNING_SEP):
276 538ca33a Tsachy Shacham
    if range_def == constants.CPU_PINNING_ALL:
277 538ca33a Tsachy Shacham
      cpu_list.append([constants.CPU_PINNING_ALL_VAL, ])
278 538ca33a Tsachy Shacham
    else:
279 538ca33a Tsachy Shacham
      # Uniquify and sort the list before adding
280 538ca33a Tsachy Shacham
      cpu_list.append(sorted(set(ParseCpuMask(range_def))))
281 538ca33a Tsachy Shacham
282 538ca33a Tsachy Shacham
  return cpu_list
283 538ca33a Tsachy Shacham
284 538ca33a Tsachy Shacham
285 257f4c0a Iustin Pop
def GetHomeDir(user, default=None):
286 257f4c0a Iustin Pop
  """Try to get the homedir of the given user.
287 257f4c0a Iustin Pop

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

292 2f8b60b3 Iustin Pop
  """
293 2f8b60b3 Iustin Pop
  try:
294 257f4c0a Iustin Pop
    if isinstance(user, basestring):
295 257f4c0a Iustin Pop
      result = pwd.getpwnam(user)
296 257f4c0a Iustin Pop
    elif isinstance(user, (int, long)):
297 257f4c0a Iustin Pop
      result = pwd.getpwuid(user)
298 257f4c0a Iustin Pop
    else:
299 257f4c0a Iustin Pop
      raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
300 257f4c0a Iustin Pop
                                   type(user))
301 2f8b60b3 Iustin Pop
  except KeyError:
302 2f8b60b3 Iustin Pop
    return default
303 2f8b60b3 Iustin Pop
  return result.pw_dir
304 59072e7e Michael Hanselmann
305 59072e7e Michael Hanselmann
306 7b4126b7 Iustin Pop
def FirstFree(seq, base=0):
307 7b4126b7 Iustin Pop
  """Returns the first non-existing integer from seq.
308 7b4126b7 Iustin Pop

309 7b4126b7 Iustin Pop
  The seq argument should be a sorted list of positive integers. The
310 7b4126b7 Iustin Pop
  first time the index of an element is smaller than the element
311 7b4126b7 Iustin Pop
  value, the index will be returned.
312 7b4126b7 Iustin Pop

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

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

318 58885d79 Iustin Pop
  @type seq: sequence
319 58885d79 Iustin Pop
  @param seq: the sequence to be analyzed.
320 58885d79 Iustin Pop
  @type base: int
321 58885d79 Iustin Pop
  @param base: use this value as the base index of the sequence
322 58885d79 Iustin Pop
  @rtype: int
323 58885d79 Iustin Pop
  @return: the first non-used index in the sequence
324 7b4126b7 Iustin Pop

325 7b4126b7 Iustin Pop
  """
326 7b4126b7 Iustin Pop
  for idx, elem in enumerate(seq):
327 7b4126b7 Iustin Pop
    assert elem >= base, "Passed element is higher than base offset"
328 7b4126b7 Iustin Pop
    if elem > idx + base:
329 7b4126b7 Iustin Pop
      # idx is not used
330 7b4126b7 Iustin Pop
      return idx + base
331 7b4126b7 Iustin Pop
  return None
332 7b4126b7 Iustin Pop
333 7b4126b7 Iustin Pop
334 dfdc4060 Guido Trotter
def SingleWaitForFdCondition(fdobj, event, timeout):
335 dcd511c8 Guido Trotter
  """Waits for a condition to occur on the socket.
336 dcd511c8 Guido Trotter

337 dfdc4060 Guido Trotter
  Immediately returns at the first interruption.
338 dfdc4060 Guido Trotter

339 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
340 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
341 dfdc4060 Guido Trotter
  @type event: integer
342 dcd511c8 Guido Trotter
  @param event: ORed condition (see select module)
343 dcd511c8 Guido Trotter
  @type timeout: float or None
344 dcd511c8 Guido Trotter
  @param timeout: Timeout in seconds
345 dcd511c8 Guido Trotter
  @rtype: int or None
346 dcd511c8 Guido Trotter
  @return: None for timeout, otherwise occured conditions
347 dcd511c8 Guido Trotter

348 dcd511c8 Guido Trotter
  """
349 dcd511c8 Guido Trotter
  check = (event | select.POLLPRI |
350 dcd511c8 Guido Trotter
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
351 dcd511c8 Guido Trotter
352 dcd511c8 Guido Trotter
  if timeout is not None:
353 dcd511c8 Guido Trotter
    # Poller object expects milliseconds
354 dcd511c8 Guido Trotter
    timeout *= 1000
355 dcd511c8 Guido Trotter
356 dcd511c8 Guido Trotter
  poller = select.poll()
357 dfdc4060 Guido Trotter
  poller.register(fdobj, event)
358 dcd511c8 Guido Trotter
  try:
359 dfdc4060 Guido Trotter
    # TODO: If the main thread receives a signal and we have no timeout, we
360 dfdc4060 Guido Trotter
    # could wait forever. This should check a global "quit" flag or something
361 dfdc4060 Guido Trotter
    # every so often.
362 dfdc4060 Guido Trotter
    io_events = poller.poll(timeout)
363 dfdc4060 Guido Trotter
  except select.error, err:
364 dfdc4060 Guido Trotter
    if err[0] != errno.EINTR:
365 dfdc4060 Guido Trotter
      raise
366 dfdc4060 Guido Trotter
    io_events = []
367 dfdc4060 Guido Trotter
  if io_events and io_events[0][1] & check:
368 dfdc4060 Guido Trotter
    return io_events[0][1]
369 dfdc4060 Guido Trotter
  else:
370 dfdc4060 Guido Trotter
    return None
371 dfdc4060 Guido Trotter
372 dfdc4060 Guido Trotter
373 dfdc4060 Guido Trotter
class FdConditionWaiterHelper(object):
374 dfdc4060 Guido Trotter
  """Retry helper for WaitForFdCondition.
375 dfdc4060 Guido Trotter

376 dfdc4060 Guido Trotter
  This class contains the retried and wait functions that make sure
377 dfdc4060 Guido Trotter
  WaitForFdCondition can continue waiting until the timeout is actually
378 dfdc4060 Guido Trotter
  expired.
379 dfdc4060 Guido Trotter

380 dfdc4060 Guido Trotter
  """
381 dfdc4060 Guido Trotter
382 dfdc4060 Guido Trotter
  def __init__(self, timeout):
383 dfdc4060 Guido Trotter
    self.timeout = timeout
384 dfdc4060 Guido Trotter
385 dfdc4060 Guido Trotter
  def Poll(self, fdobj, event):
386 dfdc4060 Guido Trotter
    result = SingleWaitForFdCondition(fdobj, event, self.timeout)
387 dfdc4060 Guido Trotter
    if result is None:
388 dfdc4060 Guido Trotter
      raise RetryAgain()
389 dfdc4060 Guido Trotter
    else:
390 dfdc4060 Guido Trotter
      return result
391 dfdc4060 Guido Trotter
392 dfdc4060 Guido Trotter
  def UpdateTimeout(self, timeout):
393 dfdc4060 Guido Trotter
    self.timeout = timeout
394 dfdc4060 Guido Trotter
395 dfdc4060 Guido Trotter
396 dfdc4060 Guido Trotter
def WaitForFdCondition(fdobj, event, timeout):
397 dfdc4060 Guido Trotter
  """Waits for a condition to occur on the socket.
398 dfdc4060 Guido Trotter

399 dfdc4060 Guido Trotter
  Retries until the timeout is expired, even if interrupted.
400 dfdc4060 Guido Trotter

401 dfdc4060 Guido Trotter
  @type fdobj: integer or object supporting a fileno() method
402 dfdc4060 Guido Trotter
  @param fdobj: entity to wait for events on
403 dfdc4060 Guido Trotter
  @type event: integer
404 dfdc4060 Guido Trotter
  @param event: ORed condition (see select module)
405 dfdc4060 Guido Trotter
  @type timeout: float or None
406 dfdc4060 Guido Trotter
  @param timeout: Timeout in seconds
407 dfdc4060 Guido Trotter
  @rtype: int or None
408 dfdc4060 Guido Trotter
  @return: None for timeout, otherwise occured conditions
409 dfdc4060 Guido Trotter

410 dfdc4060 Guido Trotter
  """
411 dfdc4060 Guido Trotter
  if timeout is not None:
412 dfdc4060 Guido Trotter
    retrywaiter = FdConditionWaiterHelper(timeout)
413 1b429e2a Iustin Pop
    try:
414 1b429e2a Iustin Pop
      result = Retry(retrywaiter.Poll, RETRY_REMAINING_TIME, timeout,
415 1b429e2a Iustin Pop
                     args=(fdobj, event), wait_fn=retrywaiter.UpdateTimeout)
416 1b429e2a Iustin Pop
    except RetryTimeout:
417 1b429e2a Iustin Pop
      result = None
418 dfdc4060 Guido Trotter
  else:
419 dfdc4060 Guido Trotter
    result = None
420 dfdc4060 Guido Trotter
    while result is None:
421 dfdc4060 Guido Trotter
      result = SingleWaitForFdCondition(fdobj, event, timeout)
422 dfdc4060 Guido Trotter
  return result
423 2de64672 Iustin Pop
424 2de64672 Iustin Pop
425 2826b361 Guido Trotter
def EnsureDaemon(name):
426 2826b361 Guido Trotter
  """Check for and start daemon if not alive.
427 2826b361 Guido Trotter

428 2826b361 Guido Trotter
  """
429 2826b361 Guido Trotter
  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
430 2826b361 Guido Trotter
  if result.failed:
431 2826b361 Guido Trotter
    logging.error("Can't start daemon '%s', failure %s, output: %s",
432 2826b361 Guido Trotter
                  name, result.fail_reason, result.output)
433 2826b361 Guido Trotter
    return False
434 2826b361 Guido Trotter
435 2826b361 Guido Trotter
  return True
436 b330ac0b Guido Trotter
437 b330ac0b Guido Trotter
438 db147305 Tom Limoncelli
def StopDaemon(name):
439 db147305 Tom Limoncelli
  """Stop daemon
440 db147305 Tom Limoncelli

441 db147305 Tom Limoncelli
  """
442 db147305 Tom Limoncelli
  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
443 db147305 Tom Limoncelli
  if result.failed:
444 db147305 Tom Limoncelli
    logging.error("Can't stop daemon '%s', failure %s, output: %s",
445 db147305 Tom Limoncelli
                  name, result.fail_reason, result.output)
446 db147305 Tom Limoncelli
    return False
447 db147305 Tom Limoncelli
448 db147305 Tom Limoncelli
  return True
449 db147305 Tom Limoncelli
450 db147305 Tom Limoncelli
451 8d1a2a64 Michael Hanselmann
def CheckVolumeGroupSize(vglist, vgname, minsize):
452 8d1a2a64 Michael Hanselmann
  """Checks if the volume group list is valid.
453 8d1a2a64 Michael Hanselmann

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

457 58885d79 Iustin Pop
  @type vglist: dict
458 58885d79 Iustin Pop
  @param vglist: dictionary of volume group names and their size
459 58885d79 Iustin Pop
  @type vgname: str
460 58885d79 Iustin Pop
  @param vgname: the volume group we should check
461 58885d79 Iustin Pop
  @type minsize: int
462 58885d79 Iustin Pop
  @param minsize: the minimum size we accept
463 58885d79 Iustin Pop
  @rtype: None or str
464 58885d79 Iustin Pop
  @return: None for success, otherwise the error message
465 8d1a2a64 Michael Hanselmann

466 8d1a2a64 Michael Hanselmann
  """
467 8d1a2a64 Michael Hanselmann
  vgsize = vglist.get(vgname, None)
468 8d1a2a64 Michael Hanselmann
  if vgsize is None:
469 8d1a2a64 Michael Hanselmann
    return "volume group '%s' missing" % vgname
470 8d1a2a64 Michael Hanselmann
  elif vgsize < minsize:
471 8d1a2a64 Michael Hanselmann
    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
472 8d1a2a64 Michael Hanselmann
            (vgname, minsize, vgsize))
473 8d1a2a64 Michael Hanselmann
  return None
474 7996a135 Iustin Pop
475 7996a135 Iustin Pop
476 45bc5e4a Michael Hanselmann
def SplitTime(value):
477 739be818 Michael Hanselmann
  """Splits time as floating point number into a tuple.
478 739be818 Michael Hanselmann

479 45bc5e4a Michael Hanselmann
  @param value: Time in seconds
480 45bc5e4a Michael Hanselmann
  @type value: int or float
481 45bc5e4a Michael Hanselmann
  @return: Tuple containing (seconds, microseconds)
482 739be818 Michael Hanselmann

483 739be818 Michael Hanselmann
  """
484 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = divmod(int(value * 1000000), 1000000)
485 45bc5e4a Michael Hanselmann
486 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
487 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
488 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
489 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
490 45bc5e4a Michael Hanselmann
491 45bc5e4a Michael Hanselmann
  return (int(seconds), int(microseconds))
492 739be818 Michael Hanselmann
493 739be818 Michael Hanselmann
494 739be818 Michael Hanselmann
def MergeTime(timetuple):
495 739be818 Michael Hanselmann
  """Merges a tuple into time as a floating point number.
496 739be818 Michael Hanselmann

497 45bc5e4a Michael Hanselmann
  @param timetuple: Time as tuple, (seconds, microseconds)
498 739be818 Michael Hanselmann
  @type timetuple: tuple
499 739be818 Michael Hanselmann
  @return: Time as a floating point number expressed in seconds
500 739be818 Michael Hanselmann

501 739be818 Michael Hanselmann
  """
502 45bc5e4a Michael Hanselmann
  (seconds, microseconds) = timetuple
503 739be818 Michael Hanselmann
504 45bc5e4a Michael Hanselmann
  assert 0 <= seconds, \
505 45bc5e4a Michael Hanselmann
    "Seconds must be larger than or equal to 0, but are %s" % seconds
506 45bc5e4a Michael Hanselmann
  assert 0 <= microseconds <= 999999, \
507 45bc5e4a Michael Hanselmann
    "Microseconds must be 0-999999, but are %s" % microseconds
508 739be818 Michael Hanselmann
509 45bc5e4a Michael Hanselmann
  return float(seconds) + (float(microseconds) * 0.000001)
510 739be818 Michael Hanselmann
511 739be818 Michael Hanselmann
512 691c81b7 Michael Hanselmann
def FindMatch(data, name):
513 691c81b7 Michael Hanselmann
  """Tries to find an item in a dictionary matching a name.
514 691c81b7 Michael Hanselmann

515 691c81b7 Michael Hanselmann
  Callers have to ensure the data names aren't contradictory (e.g. a regexp
516 691c81b7 Michael Hanselmann
  that matches a string). If the name isn't a direct key, all regular
517 691c81b7 Michael Hanselmann
  expression objects in the dictionary are matched against it.
518 691c81b7 Michael Hanselmann

519 691c81b7 Michael Hanselmann
  @type data: dict
520 691c81b7 Michael Hanselmann
  @param data: Dictionary containing data
521 691c81b7 Michael Hanselmann
  @type name: string
522 691c81b7 Michael Hanselmann
  @param name: Name to look for
523 691c81b7 Michael Hanselmann
  @rtype: tuple; (value in dictionary, matched groups as list)
524 691c81b7 Michael Hanselmann

525 691c81b7 Michael Hanselmann
  """
526 691c81b7 Michael Hanselmann
  if name in data:
527 691c81b7 Michael Hanselmann
    return (data[name], [])
528 691c81b7 Michael Hanselmann
529 691c81b7 Michael Hanselmann
  for key, value in data.items():
530 691c81b7 Michael Hanselmann
    # Regex objects
531 691c81b7 Michael Hanselmann
    if hasattr(key, "match"):
532 691c81b7 Michael Hanselmann
      m = key.match(name)
533 691c81b7 Michael Hanselmann
      if m:
534 691c81b7 Michael Hanselmann
        return (value, list(m.groups()))
535 691c81b7 Michael Hanselmann
536 691c81b7 Michael Hanselmann
  return None
537 691c81b7 Michael Hanselmann
538 691c81b7 Michael Hanselmann
539 1b045f5d Balazs Lecz
def GetMounts(filename=constants.PROC_MOUNTS):
540 1b045f5d Balazs Lecz
  """Returns the list of mounted filesystems.
541 1b045f5d Balazs Lecz

542 1b045f5d Balazs Lecz
  This function is Linux-specific.
543 1b045f5d Balazs Lecz

544 1b045f5d Balazs Lecz
  @param filename: path of mounts file (/proc/mounts by default)
545 1b045f5d Balazs Lecz
  @rtype: list of tuples
546 1b045f5d Balazs Lecz
  @return: list of mount entries (device, mountpoint, fstype, options)
547 1b045f5d Balazs Lecz

548 1b045f5d Balazs Lecz
  """
549 1b045f5d Balazs Lecz
  # TODO(iustin): investigate non-Linux options (e.g. via mount output)
550 1b045f5d Balazs Lecz
  data = []
551 1b045f5d Balazs Lecz
  mountlines = ReadFile(filename).splitlines()
552 1b045f5d Balazs Lecz
  for line in mountlines:
553 1b045f5d Balazs Lecz
    device, mountpoint, fstype, options, _ = line.split(None, 4)
554 1b045f5d Balazs Lecz
    data.append((device, mountpoint, fstype, options))
555 1b045f5d Balazs Lecz
556 1b045f5d Balazs Lecz
  return data
557 1b045f5d Balazs Lecz
558 1b045f5d Balazs Lecz
559 451575de Guido Trotter
def SignalHandled(signums):
560 451575de Guido Trotter
  """Signal Handled decoration.
561 451575de Guido Trotter

562 451575de Guido Trotter
  This special decorator installs a signal handler and then calls the target
563 451575de Guido Trotter
  function. The function must accept a 'signal_handlers' keyword argument,
564 451575de Guido Trotter
  which will contain a dict indexed by signal number, with SignalHandler
565 451575de Guido Trotter
  objects as values.
566 451575de Guido Trotter

567 451575de Guido Trotter
  The decorator can be safely stacked with iself, to handle multiple signals
568 451575de Guido Trotter
  with different handlers.
569 451575de Guido Trotter

570 451575de Guido Trotter
  @type signums: list
571 451575de Guido Trotter
  @param signums: signals to intercept
572 451575de Guido Trotter

573 451575de Guido Trotter
  """
574 451575de Guido Trotter
  def wrap(fn):
575 451575de Guido Trotter
    def sig_function(*args, **kwargs):
576 d0c8c01d Iustin Pop
      assert "signal_handlers" not in kwargs or \
577 d0c8c01d Iustin Pop
             kwargs["signal_handlers"] is None or \
578 d0c8c01d Iustin Pop
             isinstance(kwargs["signal_handlers"], dict), \
579 451575de Guido Trotter
             "Wrong signal_handlers parameter in original function call"
580 d0c8c01d Iustin Pop
      if "signal_handlers" in kwargs and kwargs["signal_handlers"] is not None:
581 d0c8c01d Iustin Pop
        signal_handlers = kwargs["signal_handlers"]
582 451575de Guido Trotter
      else:
583 451575de Guido Trotter
        signal_handlers = {}
584 d0c8c01d Iustin Pop
        kwargs["signal_handlers"] = signal_handlers
585 451575de Guido Trotter
      sighandler = SignalHandler(signums)
586 451575de Guido Trotter
      try:
587 451575de Guido Trotter
        for sig in signums:
588 451575de Guido Trotter
          signal_handlers[sig] = sighandler
589 451575de Guido Trotter
        return fn(*args, **kwargs)
590 451575de Guido Trotter
      finally:
591 451575de Guido Trotter
        sighandler.Reset()
592 451575de Guido Trotter
    return sig_function
593 451575de Guido Trotter
  return wrap
594 451575de Guido Trotter
595 451575de Guido Trotter
596 f8326fca Andrea Spadaccini
def TimeoutExpired(epoch, timeout, _time_fn=time.time):
597 f8326fca Andrea Spadaccini
  """Checks whether a timeout has expired.
598 f8326fca Andrea Spadaccini

599 f8326fca Andrea Spadaccini
  """
600 f8326fca Andrea Spadaccini
  return _time_fn() > (epoch + timeout)
601 f8326fca Andrea Spadaccini
602 f8326fca Andrea Spadaccini
603 b9768937 Michael Hanselmann
class SignalWakeupFd(object):
604 b9768937 Michael Hanselmann
  try:
605 b9768937 Michael Hanselmann
    # This is only supported in Python 2.5 and above (some distributions
606 b9768937 Michael Hanselmann
    # backported it to Python 2.4)
607 b9768937 Michael Hanselmann
    _set_wakeup_fd_fn = signal.set_wakeup_fd
608 b9768937 Michael Hanselmann
  except AttributeError:
609 b9768937 Michael Hanselmann
    # Not supported
610 b459a848 Andrea Spadaccini
    def _SetWakeupFd(self, _): # pylint: disable=R0201
611 b9768937 Michael Hanselmann
      return -1
612 b9768937 Michael Hanselmann
  else:
613 b9768937 Michael Hanselmann
    def _SetWakeupFd(self, fd):
614 b9768937 Michael Hanselmann
      return self._set_wakeup_fd_fn(fd)
615 b9768937 Michael Hanselmann
616 b9768937 Michael Hanselmann
  def __init__(self):
617 b9768937 Michael Hanselmann
    """Initializes this class.
618 b9768937 Michael Hanselmann

619 b9768937 Michael Hanselmann
    """
620 b9768937 Michael Hanselmann
    (read_fd, write_fd) = os.pipe()
621 b9768937 Michael Hanselmann
622 b9768937 Michael Hanselmann
    # Once these succeeded, the file descriptors will be closed automatically.
623 b9768937 Michael Hanselmann
    # Buffer size 0 is important, otherwise .read() with a specified length
624 b9768937 Michael Hanselmann
    # might buffer data and the file descriptors won't be marked readable.
625 b9768937 Michael Hanselmann
    self._read_fh = os.fdopen(read_fd, "r", 0)
626 b9768937 Michael Hanselmann
    self._write_fh = os.fdopen(write_fd, "w", 0)
627 b9768937 Michael Hanselmann
628 b9768937 Michael Hanselmann
    self._previous = self._SetWakeupFd(self._write_fh.fileno())
629 b9768937 Michael Hanselmann
630 b9768937 Michael Hanselmann
    # Utility functions
631 b9768937 Michael Hanselmann
    self.fileno = self._read_fh.fileno
632 b9768937 Michael Hanselmann
    self.read = self._read_fh.read
633 b9768937 Michael Hanselmann
634 b9768937 Michael Hanselmann
  def Reset(self):
635 b9768937 Michael Hanselmann
    """Restores the previous wakeup file descriptor.
636 b9768937 Michael Hanselmann

637 b9768937 Michael Hanselmann
    """
638 b9768937 Michael Hanselmann
    if hasattr(self, "_previous") and self._previous is not None:
639 b9768937 Michael Hanselmann
      self._SetWakeupFd(self._previous)
640 b9768937 Michael Hanselmann
      self._previous = None
641 b9768937 Michael Hanselmann
642 b9768937 Michael Hanselmann
  def Notify(self):
643 b9768937 Michael Hanselmann
    """Notifies the wakeup file descriptor.
644 b9768937 Michael Hanselmann

645 b9768937 Michael Hanselmann
    """
646 b9768937 Michael Hanselmann
    self._write_fh.write("\0")
647 b9768937 Michael Hanselmann
648 b9768937 Michael Hanselmann
  def __del__(self):
649 b9768937 Michael Hanselmann
    """Called before object deletion.
650 b9768937 Michael Hanselmann

651 b9768937 Michael Hanselmann
    """
652 b9768937 Michael Hanselmann
    self.Reset()
653 b9768937 Michael Hanselmann
654 b9768937 Michael Hanselmann
655 de499029 Michael Hanselmann
class SignalHandler(object):
656 de499029 Michael Hanselmann
  """Generic signal handler class.
657 de499029 Michael Hanselmann

658 58885d79 Iustin Pop
  It automatically restores the original handler when deconstructed or
659 58885d79 Iustin Pop
  when L{Reset} is called. You can either pass your own handler
660 58885d79 Iustin Pop
  function in or query the L{called} attribute to detect whether the
661 58885d79 Iustin Pop
  signal was sent.
662 58885d79 Iustin Pop

663 58885d79 Iustin Pop
  @type signum: list
664 58885d79 Iustin Pop
  @ivar signum: the signals we handle
665 58885d79 Iustin Pop
  @type called: boolean
666 58885d79 Iustin Pop
  @ivar called: tracks whether any of the signals have been raised
667 de499029 Michael Hanselmann

668 de499029 Michael Hanselmann
  """
669 b9768937 Michael Hanselmann
  def __init__(self, signum, handler_fn=None, wakeup=None):
670 de499029 Michael Hanselmann
    """Constructs a new SignalHandler instance.
671 de499029 Michael Hanselmann

672 58885d79 Iustin Pop
    @type signum: int or list of ints
673 de499029 Michael Hanselmann
    @param signum: Single signal number or set of signal numbers
674 92b61ec7 Michael Hanselmann
    @type handler_fn: callable
675 92b61ec7 Michael Hanselmann
    @param handler_fn: Signal handling function
676 de499029 Michael Hanselmann

677 de499029 Michael Hanselmann
    """
678 92b61ec7 Michael Hanselmann
    assert handler_fn is None or callable(handler_fn)
679 92b61ec7 Michael Hanselmann
680 6c52849e Guido Trotter
    self.signum = set(signum)
681 de499029 Michael Hanselmann
    self.called = False
682 de499029 Michael Hanselmann
683 92b61ec7 Michael Hanselmann
    self._handler_fn = handler_fn
684 b9768937 Michael Hanselmann
    self._wakeup = wakeup
685 92b61ec7 Michael Hanselmann
686 de499029 Michael Hanselmann
    self._previous = {}
687 de499029 Michael Hanselmann
    try:
688 de499029 Michael Hanselmann
      for signum in self.signum:
689 de499029 Michael Hanselmann
        # Setup handler
690 de499029 Michael Hanselmann
        prev_handler = signal.signal(signum, self._HandleSignal)
691 de499029 Michael Hanselmann
        try:
692 de499029 Michael Hanselmann
          self._previous[signum] = prev_handler
693 de499029 Michael Hanselmann
        except:
694 de499029 Michael Hanselmann
          # Restore previous handler
695 de499029 Michael Hanselmann
          signal.signal(signum, prev_handler)
696 de499029 Michael Hanselmann
          raise
697 de499029 Michael Hanselmann
    except:
698 de499029 Michael Hanselmann
      # Reset all handlers
699 de499029 Michael Hanselmann
      self.Reset()
700 de499029 Michael Hanselmann
      # Here we have a race condition: a handler may have already been called,
701 de499029 Michael Hanselmann
      # but there's not much we can do about it at this point.
702 de499029 Michael Hanselmann
      raise
703 de499029 Michael Hanselmann
704 de499029 Michael Hanselmann
  def __del__(self):
705 de499029 Michael Hanselmann
    self.Reset()
706 de499029 Michael Hanselmann
707 de499029 Michael Hanselmann
  def Reset(self):
708 de499029 Michael Hanselmann
    """Restore previous handler.
709 de499029 Michael Hanselmann

710 58885d79 Iustin Pop
    This will reset all the signals to their previous handlers.
711 58885d79 Iustin Pop

712 de499029 Michael Hanselmann
    """
713 de499029 Michael Hanselmann
    for signum, prev_handler in self._previous.items():
714 de499029 Michael Hanselmann
      signal.signal(signum, prev_handler)
715 de499029 Michael Hanselmann
      # If successful, remove from dict
716 de499029 Michael Hanselmann
      del self._previous[signum]
717 de499029 Michael Hanselmann
718 de499029 Michael Hanselmann
  def Clear(self):
719 58885d79 Iustin Pop
    """Unsets the L{called} flag.
720 de499029 Michael Hanselmann

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

723 de499029 Michael Hanselmann
    """
724 de499029 Michael Hanselmann
    self.called = False
725 de499029 Michael Hanselmann
726 92b61ec7 Michael Hanselmann
  def _HandleSignal(self, signum, frame):
727 de499029 Michael Hanselmann
    """Actual signal handling function.
728 de499029 Michael Hanselmann

729 de499029 Michael Hanselmann
    """
730 de499029 Michael Hanselmann
    # This is not nice and not absolutely atomic, but it appears to be the only
731 de499029 Michael Hanselmann
    # solution in Python -- there are no atomic types.
732 de499029 Michael Hanselmann
    self.called = True
733 a2d2e1a7 Iustin Pop
734 b9768937 Michael Hanselmann
    if self._wakeup:
735 b9768937 Michael Hanselmann
      # Notify whoever is interested in signals
736 b9768937 Michael Hanselmann
      self._wakeup.Notify()
737 b9768937 Michael Hanselmann
738 92b61ec7 Michael Hanselmann
    if self._handler_fn:
739 92b61ec7 Michael Hanselmann
      self._handler_fn(signum, frame)
740 92b61ec7 Michael Hanselmann
741 a2d2e1a7 Iustin Pop
742 a2d2e1a7 Iustin Pop
class FieldSet(object):
743 a2d2e1a7 Iustin Pop
  """A simple field set.
744 a2d2e1a7 Iustin Pop

745 a2d2e1a7 Iustin Pop
  Among the features are:
746 a2d2e1a7 Iustin Pop
    - checking if a string is among a list of static string or regex objects
747 a2d2e1a7 Iustin Pop
    - checking if a whole list of string matches
748 a2d2e1a7 Iustin Pop
    - returning the matching groups from a regex match
749 a2d2e1a7 Iustin Pop

750 a2d2e1a7 Iustin Pop
  Internally, all fields are held as regular expression objects.
751 a2d2e1a7 Iustin Pop

752 a2d2e1a7 Iustin Pop
  """
753 a2d2e1a7 Iustin Pop
  def __init__(self, *items):
754 a2d2e1a7 Iustin Pop
    self.items = [re.compile("^%s$" % value) for value in items]
755 a2d2e1a7 Iustin Pop
756 a2d2e1a7 Iustin Pop
  def Extend(self, other_set):
757 a2d2e1a7 Iustin Pop
    """Extend the field set with the items from another one"""
758 a2d2e1a7 Iustin Pop
    self.items.extend(other_set.items)
759 a2d2e1a7 Iustin Pop
760 a2d2e1a7 Iustin Pop
  def Matches(self, field):
761 a2d2e1a7 Iustin Pop
    """Checks if a field matches the current set
762 a2d2e1a7 Iustin Pop

763 a2d2e1a7 Iustin Pop
    @type field: str
764 a2d2e1a7 Iustin Pop
    @param field: the string to match
765 6c881c52 Iustin Pop
    @return: either None or a regular expression match object
766 a2d2e1a7 Iustin Pop

767 a2d2e1a7 Iustin Pop
    """
768 a2d2e1a7 Iustin Pop
    for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
769 a2d2e1a7 Iustin Pop
      return m
770 6c881c52 Iustin Pop
    return None
771 a2d2e1a7 Iustin Pop
772 a2d2e1a7 Iustin Pop
  def NonMatching(self, items):
773 a2d2e1a7 Iustin Pop
    """Returns the list of fields not matching the current set
774 a2d2e1a7 Iustin Pop

775 a2d2e1a7 Iustin Pop
    @type items: list
776 a2d2e1a7 Iustin Pop
    @param items: the list of fields to check
777 a2d2e1a7 Iustin Pop
    @rtype: list
778 a2d2e1a7 Iustin Pop
    @return: list of non-matching fields
779 a2d2e1a7 Iustin Pop

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