Beispiel #1
0
    def deallocate_ips_by_port(self, context, port=None, **kwargs):
        ips_to_remove = []
        for addr in port["ip_addresses"]:
            if "ip_address" in kwargs:
                ip = kwargs["ip_address"]
                if ip != netaddr.IPAddress(int(addr["address"])):
                    continue

            # Note: only deallocate ip if this is the
            # only port mapped
            ips_to_remove.append(addr)

        port["ip_addresses"] = list(
            set(port["ip_addresses"]) - set(ips_to_remove))

        # NCP-1541: We don't need to track v6 IPs the same way. Also, we can't
        # delete them until we've removed the FK on the assoc record first, so
        # we have to flush the current state of the transaction.
        # NOTE(mdietz): this does increase traffic to the db because we need
        #               to flush, fetch the records again and potentially make
        #               another trip to deallocate each IP, but keeping our
        #               indices smaller probably provides more value than the
        #               cost
        # NOTE(aquillin): For floating IPs associated with the port, we do not
        #                 want to deallocate the IP or disassociate the IP from
        #                 the tenant, instead we will disassociate floating's
        #                 fixed IP address.
        context.session.flush()
        for ip in ips_to_remove:
            if ip["address_type"] == ip_types.FLOATING:
                if ip.fixed_ip:
                    db_api.floating_ip_disassociate_fixed_ip(context, ip)
                    driver = registry.DRIVER_REGISTRY.get_driver()
                    driver.remove_floating_ip(ip)
            else:
                if len(ip["ports"]) == 0:
                    self.deallocate_ip_address(context, ip)
Beispiel #2
0
    def deallocate_ips_by_port(self, context, port=None, **kwargs):
        ips_to_remove = []
        for addr in port["ip_addresses"]:
            if "ip_address" in kwargs:
                ip = kwargs["ip_address"]
                if ip != netaddr.IPAddress(int(addr["address"])):
                    continue

            # Note: only deallocate ip if this is the
            # only port mapped
            ips_to_remove.append(addr)

        port["ip_addresses"] = list(
            set(port["ip_addresses"]) - set(ips_to_remove))

        # NCP-1541: We don't need to track v6 IPs the same way. Also, we can't
        # delete them until we've removed the FK on the assoc record first, so
        # we have to flush the current state of the transaction.
        # NOTE(mdietz): this does increase traffic to the db because we need
        #               to flush, fetch the records again and potentially make
        #               another trip to deallocate each IP, but keeping our
        #               indices smaller probably provides more value than the
        #               cost
        # NOTE(aquillin): For floating IPs associated with the port, we do not
        #                 want to deallocate the IP or disassociate the IP from
        #                 the tenant, instead we will disassociate floating's
        #                 fixed IP address.
        context.session.flush()
        for ip in ips_to_remove:
            if ip["address_type"] == ip_types.FLOATING:
                if ip.fixed_ip:
                    db_api.floating_ip_disassociate_fixed_ip(context, ip)
                    driver = registry.DRIVER_REGISTRY.get_driver()
                    driver.remove_floating_ip(ip)
            else:
                if len(ip["ports"]) == 0:
                    self.deallocate_ip_address(context, ip)
Beispiel #3
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)
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 q_exc.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)
Beispiel #5
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]

    with context.session.begin():
        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)

        db_api.ip_address_deallocate(context, flip)

    if flip.fixed_ip:
        driver = registry.DRIVER_REGISTRY.get_driver()
        driver.remove_floating_ip(flip)
Beispiel #6
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)
Beispiel #7
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
Beispiel #8
0
    def deallocate_ips_by_port(self, context, port=None, **kwargs):
        ips_to_remove = []
        for addr in port["ip_addresses"]:
            if "ip_address" in kwargs:
                ip = kwargs["ip_address"]
                if ip != netaddr.IPAddress(int(addr["address"])):
                    continue

            # Note: only deallocate ip if this is the
            # only port mapped
            ips_to_remove.append(addr)

        port["ip_addresses"] = list(
            set(port["ip_addresses"]) - set(ips_to_remove))

        # NCP-1541: We don't need to track v6 IPs the same way. Also, we can't
        # delete them until we've removed the FK on the assoc record first, so
        # we have to flush the current state of the transaction.
        # NOTE(mdietz): this does increase traffic to the db because we need
        #               to flush, fetch the records again and potentially make
        #               another trip to deallocate each IP, but keeping our
        #               indices smaller probably provides more value than the
        #               cost
        # NOTE(aquillin): For floating IPs associated with the port, we do not
        #                 want to deallocate the IP or disassociate the IP from
        #                 the tenant, instead we will disassociate floating's
        #                 fixed IP address.
        context.session.flush()
        deallocated_ips = []
        flip = None
        for ip in ips_to_remove:
            if ip["address_type"] in (ip_types.FLOATING, ip_types.SCALING):
                flip = ip
            else:
                if len(ip["ports"]) == 0:
                    self.deallocate_ip_address(context, ip)
                    deallocated_ips.append(ip.id)
        if flip:
            if flip.fixed_ips and len(flip.fixed_ips) == 1:
                # This is a FLIP or SCIP that is only associated with one
                # port and fixed_ip, so we can safely just disassociate all
                # and remove the flip from unicorn.
                db_api.floating_ip_disassociate_all_fixed_ips(context, flip)
                # NOTE(blogan): I'm not too happy about having do another
                # flush but some test runs showed inconsistent state based on
                # SQLAlchemy caching.
                context.session.add(flip)
                context.session.flush()
                billing.notify(context, billing.IP_DISASSOC, flip, **kwargs)
                driver = registry.DRIVER_REGISTRY.get_driver()
                driver.remove_floating_ip(flip)
            elif len(flip.fixed_ips) > 1:
                # This is a SCIP and we need to diassociate the one fixed_ip
                # from the SCIP and update unicorn with the remaining
                # ports and fixed_ips
                remaining_fixed_ips = []
                for fix_ip in flip.fixed_ips:
                    if fix_ip.id in deallocated_ips:
                        db_api.floating_ip_disassociate_fixed_ip(
                            context, flip, fix_ip)
                        context.session.add(flip)
                        context.session.flush()
                        billing.notify(context, billing.IP_DISASSOC, flip,
                                       **kwargs)
                    else:
                        remaining_fixed_ips.append(fix_ip)
                port_fixed_ips = {}
                for fix_ip in remaining_fixed_ips:
                    # NOTE(blogan): Since this is the flip's fixed_ips it
                    # should be safe to assume there is only one port
                    # associated with it.
                    remaining_port = fix_ip.ports[0]
                    port_fixed_ips[remaining_port.id] = {
                        'port': remaining_port,
                        'fixed_ip': fix_ip
                    }
                driver = registry.DRIVER_REGISTRY.get_driver()
                driver.update_floating_ip(flip, port_fixed_ips)
Beispiel #9
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
Beispiel #10
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

    with context.session.begin():
        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 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)

    # 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)
Beispiel #11
0
    def deallocate_ips_by_port(self, context, port=None, **kwargs):
        ips_to_remove = []
        for addr in port["ip_addresses"]:
            if "ip_address" in kwargs:
                ip = kwargs["ip_address"]
                if ip != netaddr.IPAddress(int(addr["address"])):
                    continue

            # Note: only deallocate ip if this is the
            # only port mapped
            ips_to_remove.append(addr)

        port["ip_addresses"] = list(
            set(port["ip_addresses"]) - set(ips_to_remove))

        # NCP-1541: We don't need to track v6 IPs the same way. Also, we can't
        # delete them until we've removed the FK on the assoc record first, so
        # we have to flush the current state of the transaction.
        # NOTE(mdietz): this does increase traffic to the db because we need
        #               to flush, fetch the records again and potentially make
        #               another trip to deallocate each IP, but keeping our
        #               indices smaller probably provides more value than the
        #               cost
        # NOTE(aquillin): For floating IPs associated with the port, we do not
        #                 want to deallocate the IP or disassociate the IP from
        #                 the tenant, instead we will disassociate floating's
        #                 fixed IP address.
        context.session.flush()
        deallocated_ips = []
        flip = None
        for ip in ips_to_remove:
            if ip["address_type"] in (ip_types.FLOATING, ip_types.SCALING):
                flip = ip
            else:
                if len(ip["ports"]) == 0:
                    self.deallocate_ip_address(context, ip)
                    deallocated_ips.append(ip.id)
        if flip:
            if flip.fixed_ips and len(flip.fixed_ips) == 1:
                # This is a FLIP or SCIP that is only associated with one
                # port and fixed_ip, so we can safely just disassociate all
                # and remove the flip from unicorn.
                db_api.floating_ip_disassociate_all_fixed_ips(context, flip)
                # NOTE(blogan): I'm not too happy about having do another
                # flush but some test runs showed inconsistent state based on
                # SQLAlchemy caching.
                context.session.add(flip)
                context.session.flush()
                notify(context, 'ip.disassociate', flip)
                driver = registry.DRIVER_REGISTRY.get_driver()
                driver.remove_floating_ip(flip)
            elif len(flip.fixed_ips) > 1:
                # This is a SCIP and we need to diassociate the one fixed_ip
                # from the SCIP and update unicorn with the remaining
                # ports and fixed_ips
                remaining_fixed_ips = []
                for fix_ip in flip.fixed_ips:
                    if fix_ip.id in deallocated_ips:
                        db_api.floating_ip_disassociate_fixed_ip(
                            context, flip, fix_ip)
                        context.session.add(flip)
                        context.session.flush()
                        notify(context, 'ip.disassociate', flip)
                    else:
                        remaining_fixed_ips.append(fix_ip)
                port_fixed_ips = {}
                for fix_ip in remaining_fixed_ips:
                    # NOTE(blogan): Since this is the flip's fixed_ips it
                    # should be safe to assume there is only one port
                    # associated with it.
                    remaining_port = fix_ip.ports[0]
                    port_fixed_ips[remaining_port.id] = {
                        'port': remaining_port,
                        'fixed_ip': fix_ip
                    }
                driver = registry.DRIVER_REGISTRY.get_driver()
                driver.update_floating_ip(flip, port_fixed_ips)