예제 #1
0
def get_floatingip(context, id, fields=None):
    """Retrieve a floating IP.

    :param context: neutron api request context.
    :param id: The UUID of the floating IP.
    :param fields: a list of strings that are valid keys in a
        floating IP dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.

    :returns: Dictionary containing details for the floating IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """
    LOG.info('get_floatingip %s for tenant %s' % (id, context.tenant_id))

    filters = {'address_type': ip_types.FLOATING, '_deallocated': False}

    floating_ip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE,
                                          **filters)

    if not floating_ip:
        raise qex.FloatingIpNotFound(id=id)

    return v._make_floating_ip_dict(floating_ip)
예제 #2
0
def delete_floatingip(context, id):
    """deallocate a floating IP.

    :param context: neutron api request context.
    :param id: id of the floating ip
    """

    LOG.info('delete_floatingip %s for tenant %s' % (id, context.tenant_id))

    filters = {'address_type': ip_types.FLOATING, '_deallocated': False}

    flip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE, **filters)
    if not flip:
        raise qex.FloatingIpNotFound(id=id)

    current_ports = flip.ports
    current_port = None

    if current_ports and len(current_ports) > 0:
        current_port = current_ports[0]

    context.session.begin()
    try:
        strategy_name = flip.network.get('ipam_strategy')
        ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
        ipam_driver.deallocate_ip_address(context, flip)

        if current_port:
            flip = db_api.port_disassociate_ip(context, [current_port],
                                               flip)
        if flip.fixed_ip:
            flip = db_api.floating_ip_disassociate_fixed_ip(context, flip)

        context.session.commit()
    except Exception:
        context.session.rollback()
        raise

    try:
        driver = registry.DRIVER_REGISTRY.get_driver()
        driver.remove_floating_ip(flip)
    except Exception as e:
        LOG.error('There was an error when trying to delete the floating ip '
                  'on the unicorn API.  The ip has been cleaned up, but '
                  'may need to be handled manually in the unicorn API.  '
                  'Error: %s' % e.message)
예제 #3
0
def _delete_flip(context, id, address_type):
    filters = {'address_type': address_type, '_deallocated': False}

    flip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE, **filters)
    if not flip:
        raise q_exc.FloatingIpNotFound(id=id)

    current_ports = flip.ports
    if address_type == ip_types.FLOATING:
        if current_ports:
            current_ports = [flip.ports[0]]
    elif address_type == ip_types.SCALING:
        current_ports = flip.ports

    context.session.begin()
    try:
        strategy_name = flip.network.get('ipam_strategy')
        ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
        ipam_driver.deallocate_ip_address(context, flip)

        if current_ports:
            db_api.port_disassociate_ip(context, current_ports, flip)
        if flip.fixed_ips:
            db_api.floating_ip_disassociate_all_fixed_ips(context, flip)

        context.session.commit()
    except Exception:
        context.session.rollback()
        raise

    try:
        driver = registry.DRIVER_REGISTRY.get_driver()
        driver.remove_floating_ip(flip)
    except Exception as e:
        LOG.error('There was an error when trying to delete the floating ip '
                  'on the unicorn API.  The ip has been cleaned up, but '
                  'may need to be handled manually in the unicorn API.  '
                  'Error: %s' % e.message)

    # alexm: Notify from this method because we don't have the flip object
    # in the callers
    billing.notify(context, billing.IP_DISASSOC, flip)
예제 #4
0
def delete_floatingip(context, id):
    LOG.info("delete_floatingip %s for tenant %s" % (id, context.tenant_id))

    filters = {"address_type": ip_types.FLOATING, "_deallocated": False}

    floating_ip = db_api.floating_ip_find(context,
                                          id=id,
                                          scope=db_api.ONE,
                                          **filters)
    if not floating_ip:
        raise quark_exceptions.FloatingIpNotFound(id=id)

    driver_type = CONF.QUARK.default_floating_ip_driver
    driver = registry.DRIVER_REGISTRY.get_driver(driver_type)

    driver.remove_floating_ip(floating_ip)

    strategy_name = floating_ip.network.get("ipam_strategy")
    ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
    ipam_driver.deallocate_ip_address(context, floating_ip)
예제 #5
0
def update_floatingip(context, id, content):
    """Update an existing floating IP.

    :param context: neutron api request context.
    :param id: id of the floating ip
    :param content: dictionary with keys indicating fields to update.
        valid keys are those that have a value of True for 'allow_put'
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.

    :returns: Dictionary containing details for the new floating IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """

    LOG.info('update_floatingip %s for tenant %s and body %s' %
             (id, context.tenant_id, content))

    if 'port_id' not in content:
        raise exceptions.BadRequest(resource='floating_ip',
                                    msg='port_id is required.')

    port_id = content.get('port_id')
    port = None
    fixed_ip = None
    current_port = None

    context.session.begin()
    try:
        flip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE)
        if not flip:
            raise qex.FloatingIpNotFound(id=id)

        current_ports = flip.ports

        if current_ports and len(current_ports) > 0:
            current_port = current_ports[0]

        if not port_id and not current_port:
            raise qex.FloatingIpUpdateNoPortIdSupplied()

        if port_id:
            port = db_api.port_find(context, id=port_id, scope=db_api.ONE)
            if not port:
                raise exceptions.PortNotFound(port_id=port_id)

            if any(ip for ip in port.ip_addresses
                   if (ip.get('address_type') == ip_types.FLOATING)):
                raise qex.PortAlreadyContainsFloatingIp(port_id=port_id)

            if current_port and current_port.id == port_id:
                d = dict(flip_id=id, port_id=port_id)
                raise qex.PortAlreadyAssociatedToFloatingIp(**d)

            fixed_ip = _get_next_available_fixed_ip(port)
            LOG.info('new fixed ip: %s' % fixed_ip)
            if not fixed_ip:
                raise qex.NoAvailableFixedIpsForPort(port_id=port_id)

        LOG.info('current ports: %s' % current_ports)

        if current_port:
            flip = db_api.port_disassociate_ip(context, [current_port], flip)

        if flip.fixed_ip:
            flip = db_api.floating_ip_disassociate_fixed_ip(context, flip)

        if port:
            flip = db_api.port_associate_ip(context, [port], flip, [port_id])
            flip = db_api.floating_ip_associate_fixed_ip(context, flip,
                                                         fixed_ip)

        flip_driver = registry.DRIVER_REGISTRY.get_driver()

        if port:
            if current_port:
                flip_driver.update_floating_ip(flip, port, fixed_ip)
            else:
                flip_driver.register_floating_ip(flip, port, fixed_ip)
        else:
            flip_driver.remove_floating_ip(flip)

        context.session.commit()
    except (qex.RegisterFloatingIpFailure, qex.RemoveFloatingIpFailure):
        context.session.rollback()
        raise

    # Note(alanquillin) The ports parameters on the model is not
    # properly getting cleaned up when removed.  Manually cleaning them up.
    # Need to fix the db api to correctly update the model.
    if not port:
        flip.ports = []

    return v._make_floating_ip_dict(flip, port_id)
예제 #6
0
def _update_flip(context, flip_id, ip_type, requested_ports):
    """Update a flip based IPAddress

    :param context: neutron api request context.
    :param flip_id: id of the flip or scip
    :param ip_type: ip_types.FLOATING | ip_types.SCALING
    :param requested_ports: dictionary of the structure:
    {"port_id": "<id of port>", "fixed_ip": "<fixed ip address>"}
    :return: quark.models.IPAddress
    """
    # This list will hold flips that require notifications.
    # Using sets to avoid dups, if any.
    notifications = {billing.IP_ASSOC: set(), billing.IP_DISASSOC: set()}

    context.session.begin()
    try:
        flip = db_api.floating_ip_find(context, id=flip_id, scope=db_api.ONE)
        if not flip:
            if ip_type == ip_types.SCALING:
                raise q_exc.ScalingIpNotFound(id=flip_id)
            raise q_exc.FloatingIpNotFound(id=flip_id)
        current_ports = flip.ports

        # Determine what ports are being removed, being added, and remain
        req_port_ids = [
            request_port.get('port_id') for request_port in requested_ports
        ]
        curr_port_ids = [curr_port.id for curr_port in current_ports]
        added_port_ids = [
            port_id for port_id in req_port_ids
            if port_id and port_id not in curr_port_ids
        ]
        removed_port_ids = [
            port_id for port_id in curr_port_ids if port_id not in req_port_ids
        ]
        remaining_port_ids = set(curr_port_ids) - set(removed_port_ids)

        # Validations just for floating ip types
        if (ip_type == ip_types.FLOATING and curr_port_ids
                and curr_port_ids == req_port_ids):
            d = dict(flip_id=flip_id, port_id=curr_port_ids[0])
            raise q_exc.PortAlreadyAssociatedToFloatingIp(**d)
        if (ip_type == ip_types.FLOATING and not curr_port_ids
                and not req_port_ids):
            raise q_exc.FloatingIpUpdateNoPortIdSupplied()

        # Validate that GW IP is not in use on the NW.
        flip_subnet = v._make_subnet_dict(flip.subnet)
        for added_port_id in added_port_ids:
            port = _get_port(context, added_port_id)
            nw = port.network
            nw_ports = v._make_ports_list(nw.ports)
            fixed_ips = [
                ip.get('ip_address') for p in nw_ports
                for ip in p.get('fixed_ips')
            ]

            gw_ip = flip_subnet.get('gateway_ip')
            if gw_ip in fixed_ips:
                port_with_gateway_ip = None
                for port in nw_ports:
                    for ip in port.get('fixed_ips'):
                        if gw_ip in ip.get('ip_address'):
                            port_with_gateway_ip = port
                            break
                port_id = port_with_gateway_ip.get('id')
                network_id = port_with_gateway_ip.get('network_id')
                raise q_exc.FixedIpAllocatedToGatewayIp(port_id=port_id,
                                                        network_id=network_id)
        port_fixed_ips = {}

        # Keep the ports and fixed ips that have not changed
        for port_id in remaining_port_ids:
            port = db_api.port_find(context, id=port_id, scope=db_api.ONE)
            fixed_ip = _get_flip_fixed_ip_by_port_id(flip, port_id)
            port_fixed_ips[port_id] = {'port': port, 'fixed_ip': fixed_ip}

        # Disassociate the ports and fixed ips from the flip that were
        # associated to the flip but are not anymore
        for port_id in removed_port_ids:
            port = db_api.port_find(context, id=port_id, scope=db_api.ONE)
            flip = db_api.port_disassociate_ip(context, [port], flip)
            notifications[billing.IP_DISASSOC].add(flip)
            fixed_ip = _get_flip_fixed_ip_by_port_id(flip, port_id)
            if fixed_ip:
                flip = db_api.floating_ip_disassociate_fixed_ip(
                    context, flip, fixed_ip)

        # Validate the new ports with the flip and associate the new ports
        # and fixed ips with the flip
        for port_id in added_port_ids:
            port = db_api.port_find(context, id=port_id, scope=db_api.ONE)
            if not port:
                raise n_exc.PortNotFound(port_id=port_id)
            if any(ip for ip in port.ip_addresses
                   if (ip.get('address_type') == ip_types.FLOATING)):
                raise q_exc.PortAlreadyContainsFloatingIp(port_id=port_id)
            if any(ip for ip in port.ip_addresses
                   if (ip.get('address_type') == ip_types.SCALING)):
                raise q_exc.PortAlreadyContainsScalingIp(port_id=port_id)
            fixed_ip = _get_next_available_fixed_ip(port)
            LOG.info('new fixed ip: %s' % fixed_ip)
            if not fixed_ip:
                raise q_exc.NoAvailableFixedIpsForPort(port_id=port_id)
            port_fixed_ips[port_id] = {'port': port, 'fixed_ip': fixed_ip}
            flip = db_api.port_associate_ip(context, [port], flip, [port_id])
            notifications[billing.IP_ASSOC].add(flip)
            flip = db_api.floating_ip_associate_fixed_ip(
                context, flip, fixed_ip)

        flip_driver = registry.DRIVER_REGISTRY.get_driver()
        # If there are not any remaining ports and no new ones are being added,
        # remove the floating ip from unicorn
        if not remaining_port_ids and not added_port_ids:
            flip_driver.remove_floating_ip(flip)
        # If new ports are being added but there previously was not any ports,
        # then register a new floating ip with the driver because it is
        # assumed it does not exist
        elif added_port_ids and not curr_port_ids:
            flip_driver.register_floating_ip(flip, port_fixed_ips)
        else:
            flip_driver.update_floating_ip(flip, port_fixed_ips)
        context.session.commit()
    except Exception:
        context.session.rollback()
        raise

    # Send notifications for possible associate/disassociate events
    for notif_type, flip_set in notifications.iteritems():
        for flip in flip_set:
            billing.notify(context, notif_type, flip)

    # NOTE(blogan): ORM does not seem to update the model to the real state
    # of the database, so I'm doing an explicit refresh for now.
    context.session.refresh(flip)
    return flip