<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def a10_allocate_ip_from_dhcp_range(self, subnet, interface_id, mac, port_id):
 """Search for an available IP.addr from unallocated nmodels.IPAllocationPool range. If no addresses are available then an error is raised. Returns the address as a string. This search is conducted by a difference of the nmodels.IPAllocationPool set_a and the current IP allocations. """

        subnet_id = subnet["id"]
        network_id = subnet["network_id"]

        iprange_result = self.get_ipallocationpool_by_subnet_id(subnet_id)
        ip_in_use_list = [x.ip_address for x in self.get_ipallocations_by_subnet_id(subnet_id)]

        range_begin, range_end = iprange_result.first_ip, iprange_result.last_ip
        ip_address = IPHelpers.find_unused_ip(range_begin, range_end, ip_in_use_list)

        if not ip_address:
            msg = "Cannot allocate from subnet {0}".format(subnet)
            LOG.error(msg)
            # TODO(mdurrant) - Raise neutron exception
            raise Exception

        mark_in_use = {
            "ip_address": ip_address,
            "network_id": network_id,
            "port_id": port_id,
            "subnet_id": subnet["id"]
        }

        self.create_ipallocation(mark_in_use)

        return ip_address, subnet["cidr"], mark_in_use["port_id"]