Revision d242923c

b/lib/utils/text.py
37 37
#: Characters which don't need to be quoted for shell commands
38 38
_SHELL_UNQUOTED_RE = re.compile("^[-.,=:/_+@A-Za-z0-9]+$")
39 39

  
40
#: MAC checker regexp
41
_MAC_CHECK_RE = re.compile("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", re.I)
42

  
43 40
#: Shell param checker regexp
44 41
_SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
45 42

  
46 43
#: ASCII equivalent of unicode character 'HORIZONTAL ELLIPSIS' (U+2026)
47 44
_ASCII_ELLIPSIS = "..."
48 45

  
46
#: MAC address octet
47
_MAC_ADDR_OCTET_RE = r"[0-9a-f]{2}"
48

  
49 49

  
50 50
def MatchNameComponent(key, name_list, case_sensitive=True):
51 51
  """Try to match a name against a list.
......
301 301
  return os.urandom(numbytes).encode("hex")
302 302

  
303 303

  
304
def NormalizeAndValidateMac(mac):
305
  """Normalizes and check if a MAC address is valid.
304
def _MakeMacAddrRegexp(octets):
305
  """Builds a regular expression for verifying MAC addresses.
306 306

  
307
  Checks whether the supplied MAC address is formally correct, only
308
  accepts colon separated format. Normalize it to all lower.
307
  @type octets: integer
308
  @param octets: How many octets to expect (1-6)
309
  @return: Compiled regular expression
309 310

  
310
  @type mac: str
311
  @param mac: the MAC to be validated
312
  @rtype: str
313
  @return: returns the normalized and validated MAC.
311
  """
312
  assert octets > 0
313
  assert octets <= 6
314

  
315
  return re.compile("^%s$" % ":".join([_MAC_ADDR_OCTET_RE] * octets),
316
                    re.I)
317

  
318

  
319
#: Regular expression for full MAC address
320
_MAC_CHECK_RE = _MakeMacAddrRegexp(6)
321

  
322
#: Regular expression for half a MAC address
323
_MAC_PREFIX_CHECK_RE = _MakeMacAddrRegexp(3)
324

  
325

  
326
def _MacAddressCheck(check_re, mac, msg):
327
  """Checks a MAC address using a regular expression.
328

  
329
  @param check_re: Compiled regular expression as returned by C{re.compile}
330
  @type mac: string
331
  @param mac: MAC address to be validated
332
  @type msg: string
333
  @param msg: Error message (%s will be replaced with MAC address)
334

  
335
  """
336
  if check_re.match(mac):
337
    return mac.lower()
338

  
339
  raise errors.OpPrereqError(msg % mac, errors.ECODE_INVAL)
314 340

  
315
  @raise errors.OpPrereqError: If the MAC isn't valid
341

  
342
def NormalizeAndValidateMac(mac):
343
  """Normalizes and check if a MAC address is valid and contains six octets.
344

  
345
  Checks whether the supplied MAC address is formally correct. Accepts
346
  colon-separated format only. Normalize it to all lower case.
347

  
348
  @type mac: string
349
  @param mac: MAC address to be validated
350
  @rtype: string
351
  @return: Normalized and validated MAC address
352
  @raise errors.OpPrereqError: If the MAC address isn't valid
316 353

  
317 354
  """
318
  if not _MAC_CHECK_RE.match(mac):
319
    raise errors.OpPrereqError("Invalid MAC address '%s'" % mac,
320
                               errors.ECODE_INVAL)
355
  return _MacAddressCheck(_MAC_CHECK_RE, mac, "Invalid MAC address '%s'")
321 356

  
322
  return mac.lower()
357

  
358
def NormalizeAndValidateThreeOctetMacPrefix(mac):
359
  """Normalizes a potential MAC address prefix (three octets).
360

  
361
  Checks whether the supplied string is a valid MAC address prefix consisting
362
  of three colon-separated octets. The result is normalized to all lower case.
363

  
364
  @type mac: string
365
  @param mac: Prefix to be validated
366
  @rtype: string
367
  @return: Normalized and validated prefix
368
  @raise errors.OpPrereqError: If the MAC address prefix isn't valid
369

  
370
  """
371
  return _MacAddressCheck(_MAC_PREFIX_CHECK_RE, mac,
372
                          "Invalid MAC address prefix '%s'")
323 373

  
324 374

  
325 375
def SafeEncode(text):
b/test/ganeti.utils.text_unittest.py
356 356

  
357 357
class TestNormalizeAndValidateMac(unittest.TestCase):
358 358
  def testInvalid(self):
359
    self.assertRaises(errors.OpPrereqError,
360
                      utils.NormalizeAndValidateMac, "xxx")
359
    for i in ["xxx", "00:11:22:33:44:55:66", "zz:zz:zz:zz:zz:zz"]:
360
      self.assertRaises(errors.OpPrereqError, utils.NormalizeAndValidateMac, i)
361 361

  
362 362
  def testNormalization(self):
363 363
    for mac in ["aa:bb:cc:dd:ee:ff", "00:AA:11:bB:22:cc"]:
364 364
      self.assertEqual(utils.NormalizeAndValidateMac(mac), mac.lower())
365 365

  
366 366

  
367
class TestNormalizeAndValidateThreeOctetMacPrefix(unittest.TestCase):
368
  def testInvalid(self):
369
    for i in ["xxx", "00:11:22:33:44:55:66", "zz:zz:zz:zz:zz:zz",
370
              "aa:bb:cc:dd:ee:ff", "00:AA:11:bB:22:cc",
371
              "00:11:"]:
372
      self.assertRaises(errors.OpPrereqError,
373
                        utils.NormalizeAndValidateThreeOctetMacPrefix, i)
374

  
375
  def testNormalization(self):
376
    for mac in ["aa:bb:cc", "00:AA:11"]:
377
      self.assertEqual(utils.NormalizeAndValidateThreeOctetMacPrefix(mac),
378
                       mac.lower())
379

  
380

  
367 381
class TestSafeEncode(unittest.TestCase):
368 382
  """Test case for SafeEncode"""
369 383

  

Also available in: Unified diff