[docs]classCreateMACAddrs(AbstractPlugin):""" This plugin ensures that each VM has a unique MAC address by keeping a global counter and incrementing it. For simplicity, this plugin uses :py:class:`netaddr.EUI`. More information can be found `in the netaddr documentation <https://netaddr.readthedocs.io/en/latest/tutorial_02.html>`_. """
[docs]def__init__(self,*args,**kwargs):""" Initialize the global MAC counter and the set of existing MAC addresses. Args: *args: Arguments are not used for this object. **kwargs: Keyword arguments are not used for this object. """super().__init__(*args,**kwargs)self.starting_mac=1self.current_mac=self.starting_macself.existing_macs=set()
[docs]def_increment_mac(self):""" Increments the current MAC address by one and does wrap around checking. """self.current_mac+=1ifself.current_mac>0xFFFFFFFFFFFF:# Ran out of addresses, wrap aroundself.current_mac=1
[docs]def_convert_int_to_mac(self,numeric):""" Converts an integer to a MAC string in the format: ``"xx:xx:xx:xx:xx:xx"``. Args: numeric (int): The integer to have converted to the MAC representation. Returns: str: MAC address string in the format: ``"xx:xx:xx:xx:xx:xx"``. """assertisinstance(numeric,int)mac=EUI(numeric,version=48)# Set the representation to the standard MAC 'xx:xx:xx:xx:xx:xx' format.mac.dialect=mac_unix_expandedreturnstr(mac)
[docs]defget_unique_mac(self):""" Make sure that the current MAC hasn't been assigned outside of this plugin. If it has, increment the MAC and check again. Returns: str: A MAC address that (at this point in the graph walk) is unique. """# Make sure the current MAC hasn't already been assigned outside of# this plugin.whileself._convert_int_to_mac(self.current_mac)inself.existing_macs:self._increment_mac()mac=self.current_macself._increment_mac()returnself._convert_int_to_mac(mac)
[docs]def_find_existing_macs(self):""" Iterate over the graph and add any existing MAC addresses to the global set. """forvminself.g.get_vertices():ifvm.is_decorated_by(VMEndpoint):try:forifaceinvm.interfaces.interfaces:if"mac"iniface:self.existing_macs.add(iface["mac"])exceptAttributeError:pass
[docs]def_assign_macs(self):""" Iterate over the graph and add a MAC address for each interface that does not have one yet. """forvminself.g.get_vertices():ifvm.is_decorated_by(VMEndpoint):try:forifaceinvm.interfaces.interfaces:if"mac"notiniface:iface["mac"]=self.get_unique_mac()assertlen(iface["mac"])==17self.existing_macs.add(iface["mac"])exceptAttributeError:self.log.warning('No interfaces found on VM "%s". Cannot create any mac addresses.',vm.name,)
[docs]defrun(self,mac_addr_start=""):""" See the starting MAC address and call subsequent functions which ensure that all VM interfaces have a unique MAC address. Args: mac_addr_start (str, optional): A seed value for the initial MAC address. Must be convertible to :obj:`int`. Defaults to ``""``. Raises: ValueError: If the passed in value is not valid. """ifmac_addr_start:try:self.mac_addr_start=int(mac_addr_start,0)ifself.mac_addr_start<1orself.mac_addr_start>0xFFFFFFFFFFFF:raiseValueError("Specified starting MAC address out of range.")exceptValueError:self.log.critical("Invalid starting MAC address. ""Must be an integer in the range 1-0xFFFFFFFFFFFF.")raise# Walk the vertices to find and note any existing MACs.# A full walk here is needed so we know the first address we assign# is unique.self._find_existing_macs()self.log.info("Found %d existing mac addresses.",len(self.existing_macs))# Now begin assigning new MACs where needed.self._assign_macs()self.log.info("After creating macs, know of %d mac addresses.",len(self.existing_macs))