def test_transaction_id(self): with self.context.session.begin(): transaction = quark_db_api.transaction_create(self.context) self.assertEqual(transaction.id, 1) with self.context.session.begin(): transaction = quark_db_api.transaction_create(self.context) self.assertEqual(transaction.id, 2) with self.context.session.begin(): transaction = quark_db_api.transaction_create(self.context) self.assertEqual(transaction.id, 3)
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() LOG.info("Attempting to reallocate an IP (step 1 of 3) - [{0}]".format( utils.pretty_kwargs(network_id=net_id, port_id=port_id, version=version, segment_id=segment_id, subnets=subnets, ip_address=ip_address))) if version == 6: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. LOG.info("Identified as v6 case, deferring to IP create path") return [] sub_ids = [] if subnets: sub_ids = subnets elif segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: LOG.info("No subnets matching segment_id {0} could be " "found".format(segment_id)) raise ip_address_failure(net_id) ip_kwargs = { "network_id": net_id, "deallocated": True, "version": version, "lock_id": None, } if reuse_after is not None: ip_kwargs["reuse_after"] = reuse_after if ip_address is not None: ip_kwargs["ip_address"] = ip_address del ip_kwargs["deallocated"] if sub_ids: ip_kwargs["subnet_id"] = sub_ids ipam_log = kwargs.get('ipam_log', None) for retry in xrange(CONF.QUARK.ip_address_retry_max): attempt = None if ipam_log: attempt = ipam_log.make_entry("attempt_to_reallocate_ip") LOG.info("Attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) m = models.IPAddress update_kwargs = { m.transaction_id: transaction.id, m.address_type: kwargs.get("address_type", ip_types.FIXED), m.deallocated: False, m.deallocated_at: None, m.used_by_tenant_id: context.tenant_id, m.allocated_at: timeutils.utcnow(), } result = db_api.ip_address_reallocate( elevated, update_kwargs, **ip_kwargs) if not result: LOG.info("Couldn't update any reallocatable addresses " "given the criteria") if attempt: attempt.failed() break updated_address = db_api.ip_address_reallocate_find( elevated, transaction.id) if not updated_address: if attempt: attempt.failed() continue LOG.info("Address {0} is reallocated".format( updated_address["address_readable"])) return [updated_address] except Exception: if attempt: attempt.failed() LOG.exception("Error in reallocate ip...") finally: if attempt: attempt.end() return []
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 insert_transaction(self): with self.context.session.begin(): transaction = db_api.transaction_create(self.context) return transaction
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() LOG.info("Attempting to reallocate an IP (step 1 of 3) - [{0}]".format( utils.pretty_kwargs(network_id=net_id, port_id=port_id, version=version, segment_id=segment_id, subnets=subnets))) if version == 6: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. LOG.info("Identified as v6 case, deferring to IP create path") return [] sub_ids = [] if subnets: sub_ids = subnets elif segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: LOG.info("No subnets matching segment_id {0} could be " "found".format(segment_id)) raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "ip_address": ip_address, "version": version, } if ip_address: del ip_kwargs["deallocated"] if sub_ids: ip_kwargs["subnet_id"] = sub_ids ipam_log = kwargs.get('ipam_log', None) for retry in xrange(CONF.QUARK.ip_address_retry_max): attempt = None if ipam_log: attempt = ipam_log.make_entry("attempt_to_reallocate_ip") LOG.info("Attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) m = models.IPAddress update_kwargs = { m.transaction_id: transaction.id, m.address_type: kwargs.get("address_type", ip_types.FIXED), m.deallocated: False, m.deallocated_at: None, m.used_by_tenant_id: context.tenant_id, m.allocated_at: timeutils.utcnow(), } result = db_api.ip_address_reallocate( elevated, update_kwargs, **ip_kwargs) if not result: LOG.info("Couldn't update any reallocatable addresses " "given the criteria") if attempt: attempt.failed() break updated_address = db_api.ip_address_reallocate_find( elevated, transaction.id) if not updated_address: if attempt: attempt.failed() continue LOG.info("Address {0} is reallocated".format( updated_address["address_readable"])) return [updated_address] except Exception: if attempt: attempt.failed() LOG.exception("Error in reallocate ip...") finally: if attempt: attempt.end() return []
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)