def _allocate_address_neutron(self, instance, network_ids):
        """
        Allocates a floating/public ip address to the given instance,
        using the OpenStack Network ('Neutron') API.

        :param instance: instance to assign address to
        :param list network_id:
          List of IDs (as strings) of networks where to
          request allocation the floating IP.

        :return: public ip address
        """
        self._init_os_api()
        with OpenStackCloudProvider.__node_start_lock:
            # Note: to return *all* addresses, all parameters to
            # `neutron_client.list_floatingips()` should be left out;
            # setting them to `None` (e.g., `fixed_ip_address=None`)
            # results in an empty list...
            free_ips = [
                ip for ip in
                self.neutron_client.list_floatingips().get('floatingips')
                if (ip['floating_network_id'] in network_ids
                    # keep only unallocated IP addrs
                    and ip['fixed_ip_address'] is None
                    and ip['port_id'] is None)
            ]
            if free_ips:
                floating_ip = free_ips.pop()
                log.debug("Using existing floating IP %r", floating_ip)
            else:
                # FIXME: OpenStack Network API v2 requires that we specify
                # a network ID along with the request for a floating IP.
                # However, ElastiCluster configuration allows for multiple
                # networks to be connected to a VM, but does not give any
                # hint as to which one(s) should be used for such requests.
                # So we try them all, ignoring errors until one request
                # succeeds and hope that it's OK. One can imagine
                # scenarios where this is *not* correct, but: (1) these
                # scenarios are unlikely, and (2) the old novaclient code
                # above has not even had the concept of multiple networks
                # for floating IPs and no-one has complained in 5 years...
                for network_id in network_ids:
                    log.debug(
                        "Trying to allocate floating IP on network %s ...", network_id)
                    try:
                        floating_ip = self.neutron_client.create_floatingip({
                            'floatingip': {
                                'floating_network_id':network_id,
                            }}).get('floatingip')
                        log.debug(
                            "Allocated IP address %s on network %s",
                            floating_ip['floating_ip_address'], network_id)
                        break  # stop at first network where we get a floating IP
                    except BadNeutronRequest as err:
                        raise RuntimeError(
                            "Failed allocating floating IP on network {0}: {1}"
                            .format(network_id, err))
            if floating_ip.get('floating_ip_address', None) is None:
                raise RuntimeError(
                    "Could not allocate floating IP for VM {0}"
                    .format(instance_id))
            # wait until at least one interface is up
            interfaces = []
            # FIXMEE: no timeout!
            while not interfaces:
                interfaces = instance.interface_list()
                sleep(2)  ## FIXME: hard-coded value
            # get port ID
            for interface in interfaces:
                log.debug(
                    "Instance %s (ID: %s):"
                    " Checking if floating IP can be attached to interface %r ...",
                    instance.name, instance.id, interface)
                # if interface.net_id not in network_ids:
                #     log.debug(
                #         "Instance %s (ID: %s):"
                #         " Skipping interface %r:"
                #         " not attached to any of the requested networks.",
                #         instance.name, instance.id, interface)
                #     continue
                port_id = interface.port_id
                if port_id is None:
                    log.debug(
                        "Instance %s (ID: %s):"
                        " Skipping interface %r: no port ID!",
                        instance.name, instance.id, interface)
                    continue
                log.debug(
                    "Instance `%s` (ID: %s):"
                    " will assign floating IP to port ID %s (state: %s),"
                    " already running IP addresses %r",
                    instance.name, instance.id,
                    port_id, interface.port_state,
                    [item['ip_address'] for item in interface.fixed_ips])
                if interface.port_state != 'ACTIVE':
                    log.warn(
                        "Instance `%s` (ID: %s):"
                        " port `%s` is in state %s (epected 'ACTIVE' instead)",
                        instance.name, instance.id,
                        port_id, interface.port_state)
                break
            else:
                raise RuntimeError(
                    "Could not find port on network(s) {0}"
                    " for instance {1} (ID: {2}) to bind a floating IP to."
                    .format(network_ids, instance.name, instance.id))
            # assign floating IP to port
            floating_ip = self.neutron_client.update_floatingip(
                floating_ip['id'], {
                    'floatingip': {
                        'port_id': port_id,
                    },
                }
            ).get('floatingip')
            ip_address = floating_ip['floating_ip_address']
            log.debug("Assigned IP address %s to port %s", ip_address, port_id)

            log.info("Waiting 300s until floating IP %s is ACTIVE", ip_address)
            for i in range(300):
                _floating_ip = self.neutron_client.show_floatingip(floating_ip['id'])
                if _floating_ip['floatingip']['status'] != 'DOWN':
                    break
                sleep(1)

            # Invalidate cache for this VM, as we just assigned a new IP
            if instance.id in self._cached_instances:
                del self._cached_instances[instance.id]
        return ip_address
Allocates a floating/public ip address to the given instance,
        using the OpenStack Network ('Neutron') API.

        :param instance: instance to assign address to
        :param list network_id:
          List of IDs (as strings) of networks where to
          request allocation the floating IP.

        :return: public ip address