def allocate_mac_address(self, context, net_id, port_id, reuse_after, mac_address=None, use_forbidden_mac_range=False, **kwargs): if mac_address: mac_address = netaddr.EUI(mac_address).value kwargs.update({"network_id": net_id, "port_id": port_id, "mac_address": mac_address, "use_forbidden_mac_range": use_forbidden_mac_range}) LOG.info(("Attempting to allocate a new MAC address " "[{0}]").format(utils.pretty_kwargs(**kwargs))) for retry in xrange(CONF.QUARK.mac_address_retry_max): LOG.info("Attemping to reallocate deallocated MAC (step 1 of 3)," " attempt {0} of {1}".format( retry + 1, CONF.QUARK.mac_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) update_kwargs = { "deallocated": False, "deallocated_at": None, "transaction_id": transaction.id } filter_kwargs = { "deallocated": True, } if mac_address is not None: filter_kwargs["address"] = mac_address if reuse_after is not None: filter_kwargs["reuse_after"] = reuse_after elevated = context.elevated() result = db_api.mac_address_reallocate( elevated, update_kwargs, **filter_kwargs) if not result: break reallocated_mac = db_api.mac_address_reallocate_find( elevated, transaction.id) if reallocated_mac: dealloc = netaddr.EUI(reallocated_mac["address"]) LOG.info("Found a suitable deallocated MAC {0}".format( str(dealloc))) LOG.info("MAC assignment for port ID {0} completed " "with address {1}".format(port_id, dealloc)) return reallocated_mac except Exception: LOG.exception("Error in mac reallocate...") continue LOG.info("Couldn't find a suitable deallocated MAC, attempting " "to create a new one") # This could fail if a large chunk of MACs were chosen explicitly, # but under concurrent load enough MAC creates should iterate without # any given thread exhausting its retry count. for retry in xrange(CONF.QUARK.mac_address_retry_max): LOG.info("Attemping to find a range to create a new MAC in " "(step 2 of 3), attempt {0} of {1}".format( retry + 1, CONF.QUARK.mac_address_retry_max)) next_address = None with context.session.begin(): try: fn = db_api.mac_address_range_find_allocation_counts mac_range = \ fn(context, address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range) if not mac_range: LOG.info("No MAC ranges could be found given " "the criteria") break rng, addr_count = mac_range LOG.info("Found a MAC range {0}".format(rng["cidr"])) last = rng["last_address"] first = rng["first_address"] if (last - first + 1) <= addr_count: # Somehow, the range got filled up without us # knowing, so set the next_auto_assign to be -1 # so we never try to create new ones # in this range db_api.mac_range_update_set_full(context, rng) LOG.info("MAC range {0} is full".format(rng["cidr"])) continue if mac_address: next_address = mac_address else: next_address = rng["next_auto_assign_mac"] if next_address + 1 > rng["last_address"]: db_api.mac_range_update_set_full(context, rng) else: db_api.mac_range_update_next_auto_assign_mac( context, rng) context.session.refresh(rng) except Exception: LOG.exception("Error in updating mac range") continue # Based on the above, this should only fail if a MAC was # was explicitly chosen at some point. As such, fall through # here and get in line for a new MAC address to try try: mac_readable = str(netaddr.EUI(next_address)) LOG.info("Attempting to create new MAC {0} " "(step 3 of 3)".format(mac_readable)) with context.session.begin(): address = db_api.mac_address_create( context, address=next_address, mac_address_range_id=rng["id"]) LOG.info("MAC assignment for port ID {0} completed with " "address {1}".format(port_id, mac_readable)) return address except Exception: LOG.info("Failed to create new MAC {0}".format(mac_readable)) LOG.exception("Error in creating mac. MAC possibly duplicate") continue raise n_exc_ext.MacAddressGenerationFailure(net_id=net_id)
def allocate_mac_address(self, context, net_id, port_id, reuse_after, mac_address=None, use_forbidden_mac_range=False): if mac_address: mac_address = netaddr.EUI(mac_address).value kwargs = {"network_id": net_id, "port_id": port_id, "mac_address": mac_address, "use_forbidden_mac_range": use_forbidden_mac_range} LOG.info(("Attempting to allocate a new MAC address " "[{0}]").format(utils.pretty_kwargs(**kwargs))) for retry in xrange(CONF.QUARK.mac_address_retry_max): LOG.info("Attemping to reallocate deallocated MAC (step 1 of 3)," " attempt {0} of {1}".format( retry + 1, CONF.QUARK.mac_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) update_kwargs = { "deallocated": False, "deallocated_at": None, "transaction_id": transaction.id } filter_kwargs = { "reuse_after": reuse_after, "deallocated": True, "address": mac_address } elevated = context.elevated() result = db_api.mac_address_reallocate( elevated, update_kwargs, **filter_kwargs) if not result: break reallocated_mac = db_api.mac_address_reallocate_find( elevated, transaction.id) if reallocated_mac: dealloc = netaddr.EUI(reallocated_mac["address"]) LOG.info("Found a suitable deallocated MAC {0}".format( str(dealloc))) LOG.info("MAC assignment for port ID {0} completed " "with address {1}".format(port_id, dealloc)) return reallocated_mac except Exception: LOG.exception("Error in mac reallocate...") continue LOG.info("Couldn't find a suitable deallocated MAC, attempting " "to create a new one") # This could fail if a large chunk of MACs were chosen explicitly, # but under concurrent load enough MAC creates should iterate without # any given thread exhausting its retry count. for retry in xrange(CONF.QUARK.mac_address_retry_max): LOG.info("Attemping to find a range to create a new MAC in " "(step 2 of 3), attempt {0} of {1}".format( retry + 1, CONF.QUARK.mac_address_retry_max)) next_address = None with context.session.begin(): try: fn = db_api.mac_address_range_find_allocation_counts mac_range = \ fn(context, address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range) if not mac_range: LOG.info("No MAC ranges could be found given " "the criteria") break rng, addr_count = mac_range LOG.info("Found a MAC range {0}".format(rng["cidr"])) last = rng["last_address"] first = rng["first_address"] if (last - first + 1) <= addr_count: # Somehow, the range got filled up without us # knowing, so set the next_auto_assign to be -1 # so we never try to create new ones # in this range db_api.mac_range_update_set_full(context, rng) LOG.info("MAC range {0} is full".format(rng["cidr"])) continue if mac_address: next_address = mac_address else: next_address = rng["next_auto_assign_mac"] if next_address + 1 > rng["last_address"]: db_api.mac_range_update_set_full(context, rng) else: db_api.mac_range_update_next_auto_assign_mac( context, rng) context.session.refresh(rng) except Exception: LOG.exception("Error in updating mac range") continue # Based on the above, this should only fail if a MAC was # was explicitly chosen at some point. As such, fall through # here and get in line for a new MAC address to try try: mac_readable = str(netaddr.EUI(next_address)) LOG.info("Attempting to create new MAC {0} " "(step 3 of 3)".format(mac_readable)) with context.session.begin(): address = db_api.mac_address_create( context, address=next_address, mac_address_range_id=rng["id"]) LOG.info("MAC assignment for port ID {0} completed with " "address {1}".format(port_id, mac_readable)) return address except Exception: LOG.info("Failed to create new MAC {0}".format(mac_readable)) LOG.exception("Error in creating mac. MAC possibly duplicate") continue raise exceptions.MacAddressGenerationFailure(net_id=net_id)