'Associate floating_ip to an instance.'

@wsgi.Controller.api_version('2.1', '2.43')
@extensions.expected_errors((400, 403, 404))
@wsgi.action('addFloatingIp')
@validation.schema(floating_ips.add_floating_ip)
def _add_floating_ip(self, req, id, body):

    context = req.environ['nova.context']
    context.can(fi_policies.BASE_POLICY_NAME)
    address = body['addFloatingIp']['address']
    instance = common.get_instance(self.compute_api, context, id, expected_attrs=['flavor'])
    cached_nwinfo = instance.get_network_info()
    if (not cached_nwinfo):
        LOG.warning('Info    cache    is    %r    during    associate    with    no    nw_info    cache', instance.info_cache, instance=instance)
        msg = _('Instance    network    is    not    ready    yet')
        raise webob.exc.HTTPBadRequest(explanation=msg)
    fixed_ips = cached_nwinfo.fixed_ips()
    if (not fixed_ips):
        msg = _('No    fixed    IPs    associated    to    instance')
        raise webob.exc.HTTPBadRequest(explanation=msg)
    fixed_address = None
    if ('fixed_address' in body['addFloatingIp']):
        fixed_address = body['addFloatingIp']['fixed_address']
        for fixed in fixed_ips:
            if (fixed['address'] == fixed_address):
                break
        else:
            msg = _('Specified    fixed    address    not    assigned    to    instance')
            raise webob.exc.HTTPBadRequest(explanation=msg)
    if (not fixed_address):
        try:
            fixed_address = next((ip['address'] for ip in fixed_ips if netutils.is_valid_ipv4(ip['address'])))
        except StopIteration:
            msg = (_('Unable    to    associate    floating    IP    %(address)s    to    any    fixed    IPs    for    instance    %(id)s.    Instance    has    no    fixed    IPv4    addresses    to    associate.') % {'address': address, 'id': id})
            raise webob.exc.HTTPBadRequest(explanation=msg)
        if (len(fixed_ips) > 1):
            LOG.warning('multiple    fixed_ips    exist,    using    the    first    IPv4    fixed_ip:    %s', fixed_address)
    try:
        self.network_api.associate_floating_ip(context, instance, floating_address=address, fixed_address=fixed_address)
    except exception.FloatingIpAssociated:
        msg = _('floating    IP    is    already    associated')
        raise webob.exc.HTTPBadRequest(explanation=msg)
    except exception.FloatingIpAssociateFailed as e:
        raise webob.exc.HTTPBadRequest(explanation=e.format_message())
    except exception.NoFloatingIpInterface:
        msg = _('l3driver    call    to    add    floating    IP    failed')
        raise webob.exc.HTTPBadRequest(explanation=msg)
    except exception.InstanceUnknownCell as e:
        raise webob.exc.HTTPNotFound(explanation=e.format_message())
    except exception.FloatingIpNotFoundForAddress:
        msg = _('floating    IP    not    found')
        raise webob.exc.HTTPNotFound(explanation=msg)
    except exception.Forbidden as e:
        raise webob.exc.HTTPForbidden(explanation=e.format_message())
    except Exception as e:
        msg = (_('Unable    to    associate    floating    IP    %(address)s    to    fixed    IP    %(fixed_address)s    for    instance    %(id)s.    Error:    %(error)s') % {'address': address, 'fixed_address': fixed_address, 'id': id, 'error': e})
        LOG.exception(msg)
        raise webob.exc.HTTPBadRequest(explanation=msg)
    return webob.Response(status_int=202)
