Source code for macaddress.ei48

"""
This module includes ExtendedIdentifier48 and IdentifierError.
"""

from functools import reduce

import re

from .octet import Octet


PLAIN = re.compile("^[0-9A-Fa-f]{12}$")
HYPHEN = re.compile("^([0-9A-Fa-f]{2}[-]{1}){5}[0-9A-Fa-f]{2}$")
COLON = re.compile("^([0-9A-Fa-f]{2}[:]{1}){5}[0-9A-Fa-f]{2}$")
DOT = re.compile("^([0-9A-Fa-f]{4}[.]{1}){2}[0-9A-Fa-f]{4}$")
NOT_DIGITS = re.compile("[^0-9A-Fa-f]")
TWO_DIGITS = re.compile("[0-9a-f]{2}")
FOUR_DIGITS = re.compile("[0-9a-f]{4}")


[docs]class IdentifierError(Exception): """ ExtendedIdentifier48 raises IdentifierError if instantiated with an invalid argument. Arguments --------- message : str A human-readable error message. """ pass
[docs]class ExtendedIdentifier48(object): """ ExtendedIdentifier48 makes it easy to work with the IEEE's 48-bit extended unique identifiers (EUI) and extended local identifiers (ELI). The first 24 or 36 bits of an EUI is called an organizationally- unique identifier (OUI), while the first 24 or 36 bits of an ELI is called a company ID (CID). Visit the IEEE's website for more information on EUIs and ELIs. Helpful link: https://standards.ieee.org/products-services/regauth/tut/index.html Attributes ---------- original : str The hexadecimal identifier passed in by the user. normalized : str The hexadecimal identifier after replacing all uppercase letters with lowercase letters and removing all hypens, colons, and dots. For example, if the user passes in `A0-B1-C2-D3-E4-F5`, then ExtendedIdentifier48 will return `a0b1c2d3e4f5`. is_valid : bool Whether the user passed in a valid hexadecimal identifier. octets : list Each of the hexadecimal identifier's six octets. first_octet : Octet The hexadecimal identifier's first octet. type : str The hexadecimal identifier's type, where type is unique, local, or unknown. The two least-significant bits in the first octet of an extended identifier determine whether it is an EUI. 00 = unique. The four least-signficant bits in the first octet of an extended identifier determine whether it is an ELI. 1010 = local. has_oui : bool Whether the hexadecimal identifier has an OUI. If the identifier is an EUI, then it has an OUI. has_cid : bool Whether the hexadecimal identifier has a CID. If the identifier is an ELI, then it has a CID. decimal : int The decimal equivalent of the hexadecimal digits passed in by the user. For example, if the user passes in `A0-B1-C2-D3-E4-F5`, then ExtendedIdentifier48 will return `176685338322165`. binary : str The binary equivalent of the hexadecimal identifier passed in by the user. *The most-significant digit of each octet appears first.* For example, if the user passes in `A0-B1-C2-D3-E4-F5`, then ExtendedIdentifier48 will return `101000001011000111000010110100111110010011110101`. reverse_binary : str The reverse-binary equivalent of the hexadecimal identifier passed in by the user. *The least-significant digit of each octet appears first.* For example, if the user passes in `A0-B1-C2-D3-E4-F5`, then ExtendedIdentifier48 will return `000001011000110101000011110010110010011110101111`. Parameters ---------- identifier : str Twelve hexadecimal digits (0-9, A-F, or a-f). Raises ------ IdentifierError """ def __init__(self, identifier): self.original = identifier if not self.is_valid: raise IdentifierError("Pass in 12 hexadecimal digits.") def __repr__(self): return "ExtendedIdentifier48('{}')".format(self.original) def __str__(self): return self.normalized @property def is_valid(self): # Evaluate the hexadecimal identifier. # It must contain 12 hexadecimal digits, and it may # be in plain, hyphen, colon, or dot notation. if PLAIN.match(self.original): return True elif HYPHEN.match(self.original): return True elif COLON.match(self.original): return True elif DOT.match(self.original): return True else: return False @property def normalized(self): return NOT_DIGITS.sub("", self.original.lower()) @property def octets(self): # Create one instance of Octet for each of the # hexadecimal identifier's six octets. matches = TWO_DIGITS.findall(self.normalized) return [Octet(match) for match in matches] @property def first_octet(self): return self.octets[0] @property def type(self): # The two least-significant bits in the first octet of # an extended identifier determine whether it is an EUI. # The four least-signficant bits in the first octet of # an extended identifier determine whether it is an ELI. if self.first_octet.binary[6:] == "00": return "unique" elif self.first_octet.binary[4:] == "1010": return "local" else: return "unknown" @property def has_oui(self): # If the hexadecimal identifier is an EUI, then it has an OUI. return self.type == "unique" @property def has_cid(self): # If the hexadecimal identifier is an ELI, then it has a CID. return self.type == "local" @property def decimal(self): return int(self.normalized, base=16) @property def binary(self): binaries = map(lambda x: x.binary, self.octets) return reduce(lambda x, y: x + y, binaries) @property def reverse_binary(self): reverse_binaries = map(lambda x: x.reverse_binary, self.octets) return reduce(lambda x, y: x + y, reverse_binaries)
[docs] def to_fragments(self, bits=24): """ Returns the hexadecimal identifier's two "fragments." For an EUI, this means the 24- or 36-bit OUI as the first fragment and the remaining device- or object-specific bits as the second fragment. For an ELI, this means the 24- or 36-bit CID as the first fragment and the remaining device- or object-specific bits as the second fragment. For example, if the user passes in `A0-B1-C2-D3-E4-F5` and calls this method with either `bits=24` or no keyword argument, then ExtendedIdentifier48 will return `(a0b1c2, d3e4f5)`. If the user passes in `A0-B1-C2-D3-E4-F5` and calls this method with `bits=36`, then ExtendedIdentifier48 will return `(a0b1c2d3e, 4f5)`. Parameters ---------- bits : int The number of bits for the OUI or CID. The default value is 24. """ digits = int(bits / 4) return (self.normalized[:digits], self.normalized[digits:])
[docs] def to_plain_notation(self): """ Returns the hexadecimal identifier in plain notation (for example, `a0b1c2d3e4f5`). """ return self.normalized
[docs] def to_hyphen_notation(self): """ Returns the hexadecimal identifier in hyphen notation (for example, `a0-b1-c2-d3-e4-f5`). """ matches = TWO_DIGITS.findall(self.normalized) return "-".join(matches)
[docs] def to_colon_notation(self): """ Returns the hexadecimal identifier in colon notation (for example, `a0:b1:c2:d3:e4:f5`). """ matches = TWO_DIGITS.findall(self.normalized) return ":".join(matches)
[docs] def to_dot_notation(self): """ Returns the hexadecimal identifier in dot notation (for example, `a0b1.c2d3.e4f5`). """ matches = FOUR_DIGITS.findall(self.normalized) return ".".join(matches)