Ejemplo n.º 1
0
    def create_ip_address(self, context, ip_address):
        LOG.info("create_ip_address for tenant %s" % context.tenant_id)

        port = None
        ip_dict = ip_address["ip_address"]
        port_id = ip_dict.get('port_id')
        network_id = ip_dict.get('network_id')
        device_id = ip_dict.get('device_id')
        ip_version = ip_dict.get('version')
        ip_address = ip_dict.get('ip_address')

        if network_id and device_id:
            port = db_api.port_find(
                context, network_id=network_id, device_id=device_id,
                tenant_id=context.tenant_id, scope=db_api.ONE)
        elif 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,
                                          net_id=network_id)
        address = self.ipam_driver.allocate_ip_address(
            context,
            port['network_id'],
            port['id'],
            self.ipam_reuse_after,
            ip_version,
            ip_address)
        port["ip_addresses"].append(address)
        return self._make_ip_dict(address)
Ejemplo n.º 2
0
 def test_port_list_device_owner_found_returns_only_those(self):
     # create a network
     network = dict(name="public", tenant_id="fake", network_plugin="BASE")
     net_mod = db_api.network_create(self.context, **network)
     # create ports
     port1 = dict(network_id=net_mod["id"],
                  backend_key="1",
                  device_id="1",
                  device_owner="Doge")
     port2 = dict(network_id=net_mod["id"],
                  backend_key="1",
                  device_id="1",
                  device_owner=port1["device_owner"])
     port3 = dict(network_id=net_mod["id"],
                  backend_key="1",
                  device_id="1",
                  device_owner="network:dhcp")
     port_mod1 = db_api.port_create(self.context, **port1)
     port_mod2 = db_api.port_create(self.context, **port2)
     port_mod3 = db_api.port_create(self.context, **port3)
     res = db_api.port_find(self.context,
                            scope=db_api.ALL,
                            device_owner=port3["device_owner"])
     self.assertTrue(len(res) == 1)
     self.assertTrue(res[0]["device_owner"] == port3["device_owner"])
     res = db_api.port_find(self.context,
                            scope=db_api.ALL,
                            device_owner=port1["device_owner"])
     self.assertTrue(len(res) == 2)
     self.assertTrue(res[0]["device_owner"] == res[1]["device_owner"] ==
                     port1["device_owner"])
     db_api.network_delete(self.context, net_mod)
     db_api.port_delete(self.context, port_mod1)
     db_api.port_delete(self.context, port_mod2)
     db_api.port_delete(self.context, port_mod3)
Ejemplo n.º 3
0
 def test_port_list_device_owner_found_returns_only_those(self):
     # create a network
     network = dict(name="public", tenant_id="fake", network_plugin="BASE")
     net_mod = db_api.network_create(self.context, **network)
     # create ports
     port1 = dict(network_id=net_mod["id"], backend_key="1", device_id="1",
                  device_owner="Doge")
     port2 = dict(network_id=net_mod["id"], backend_key="1", device_id="1",
                  device_owner=port1["device_owner"])
     port3 = dict(network_id=net_mod["id"], backend_key="1", device_id="1",
                  device_owner="network:dhcp")
     port_mod1 = db_api.port_create(self.context, **port1)
     port_mod2 = db_api.port_create(self.context, **port2)
     port_mod3 = db_api.port_create(self.context, **port3)
     res = db_api.port_find(self.context, scope=db_api.ALL,
                            device_owner=port3["device_owner"])
     self.assertTrue(len(res) == 1)
     self.assertTrue(res[0]["device_owner"] == port3["device_owner"])
     res = db_api.port_find(self.context, scope=db_api.ALL,
                            device_owner=port1["device_owner"])
     self.assertTrue(len(res) == 2)
     self.assertTrue(res[0]["device_owner"] == res[1]["device_owner"] ==
                     port1["device_owner"])
     db_api.network_delete(self.context, net_mod)
     db_api.port_delete(self.context, port_mod1)
     db_api.port_delete(self.context, port_mod2)
     db_api.port_delete(self.context, port_mod3)
Ejemplo n.º 4
0
def diagnose_port(context, id, fields):
    if id == "*":
        return {"ports": [_diag_port(context, port, fields) for port in db_api.port_find(context).all()]}
    db_port = db_api.port_find(context, id=id, scope=db_api.ONE)
    if not db_port:
        raise exceptions.PortNotFound(port_id=id, net_id="")
    port = _diag_port(context, db_port, fields)
    return {"ports": port}
Ejemplo n.º 5
0
def diagnose_port(context, id, fields):
    if not context.is_admin:
        raise exceptions.NotAuthorized()

    if id == "*":
        return {'ports': [_diag_port(context, port, fields) for
                port in db_api.port_find(context).all()]}
    db_port = db_api.port_find(context, id=id, scope=db_api.ONE)
    if not db_port:
        raise exceptions.PortNotFound(port_id=id, net_id='')
    port = _diag_port(context, db_port, fields)
    return {'ports': port}
Ejemplo n.º 6
0
def disassociate_port(context, id, ip_address_id):
    """Disassociates a port from an IP address.

    : param context: neutron api request context
    : param id: UUID representing the port to disassociate.
    : param ip_address_id: UUID representing the IP address to
    disassociate.
    """
    LOG.info("disassociate_port %s for tenant %s ip_address_id %s" %
             (id, context.tenant_id, ip_address_id))
    with context.session.begin():
        port = db_api.port_find(context, id=id, ip_address_id=[ip_address_id],
                                scope=db_api.ONE)

        if not port:
            raise exceptions.PortNotFound(port_id=id, net_id='')

        the_address = [address for address in port["ip_addresses"]
                       if address["id"] == ip_address_id][0]
        port["ip_addresses"] = [address for address in port["ip_addresses"]
                                if address.id != ip_address_id]

        if len(the_address["ports"]) == 0:
            the_address["deallocated"] = 1
    return v._make_port_dict(port)
Ejemplo n.º 7
0
 def test_ports_sorted_by_created_at(self):
     # create a network
     network = dict(name="public", tenant_id="fake", network_plugin="BASE")
     net_mod = db_api.network_create(self.context, **network)
     # create ports
     port1 = dict(network_id=net_mod["id"], backend_key="1", device_id="1")
     port2 = dict(network_id=net_mod["id"], backend_key="1", device_id="1")
     port3 = dict(network_id=net_mod["id"], backend_key="1", device_id="1")
     port_mod1 = db_api.port_create(self.context, **port1)
     port_mod2 = db_api.port_create(self.context, **port2)
     port_mod3 = db_api.port_create(self.context, **port3)
     res = db_api.port_find(context=self.context,
                            limit=None,
                            sorts=['created_at'],
                            marker_obj=None,
                            fields=None,
                            scope=db_api.ALL)
     self.assertTrue(
         res[0]["created_at"] < res[1]["created_at"] < res[2]['created_at'])
     self.assertTrue(res[0]['id'] == port_mod1['id'])
     self.assertTrue(res[1]['id'] == port_mod2['id'])
     self.assertTrue(res[2]['id'] == port_mod3['id'])
     db_api.network_delete(self.context, net_mod)
     db_api.port_delete(self.context, port_mod1)
     db_api.port_delete(self.context, port_mod2)
     db_api.port_delete(self.context, port_mod3)
Ejemplo n.º 8
0
def update_ip_address(context, id, ip_address):
    LOG.info("update_ip_address %s for tenant %s" %
            (id, context.tenant_id))

    address = db_api.ip_address_find(
        context, id=id, tenant_id=context.tenant_id, scope=db_api.ONE)

    if not address:
        raise exceptions.NotFound(
            message="No IP address found with id=%s" % id)

    old_ports = address['ports']
    port_ids = ip_address['ip_address'].get('port_ids')
    if port_ids is None:
        return v._make_ip_dict(address)

    for port in old_ports:
        port['ip_addresses'].remove(address)

    if port_ids:
        ports = db_api.port_find(
            context, tenant_id=context.tenant_id, id=port_ids,
            scope=db_api.ALL)

        # NOTE: could be considered inefficient because we're converting
        #       to a list to check length. Maybe revisit
        if len(ports) != len(port_ids):
            raise exceptions.NotFound(
                message="No ports not found with ids=%s" % port_ids)
        for port in ports:
            port['ip_addresses'].extend([address])
    else:
        address["deallocated"] = 1

    return v._make_ip_dict(address)
Ejemplo n.º 9
0
def disassociate_port(context, id, ip_address_id):
    """Disassociates a port from an IP address.

    : param context: neutron api request context
    : param id: UUID representing the port to disassociate.
    : param ip_address_id: UUID representing the IP address to
    disassociate.
    """
    LOG.info("disassociate_port %s for tenant %s ip_address_id %s" %
             (id, context.tenant_id, ip_address_id))
    port = db_api.port_find(context,
                            id=id,
                            ip_address_id=[ip_address_id],
                            scope=db_api.ONE)

    if not port:
        raise exceptions.PortNotFound(port_id=id, net_id='')

    the_address = [
        address for address in port["ip_addresses"]
        if address["id"] == ip_address_id
    ][0]
    port["ip_addresses"] = [
        address for address in port["ip_addresses"]
        if address.id != ip_address_id
    ]

    if len(the_address["ports"]) == 0:
        the_address["deallocated"] = 1
    return v._make_port_dict(port)
Ejemplo n.º 10
0
def delete_port(context, id):
    """Delete a port.

    : param context: neutron api request context
    : param id: UUID representing the port to delete.
    """
    LOG.info("delete_port %s for tenant %s" %
             (id, context.tenant_id))

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

    backend_key = port["backend_key"]
    mac_address = netaddr.EUI(port["mac_address"]).value
    ipam_driver = ipam.IPAM_REGISTRY.get_strategy(
        port["network"]["ipam_strategy"])
    ipam_driver.deallocate_mac_address(context, mac_address)
    ipam_driver.deallocate_ips_by_port(
        context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)

    net_driver = registry.DRIVER_REGISTRY.get_driver(
        port.network["network_plugin"])
    net_driver.delete_port(context, backend_key, device_id=port["device_id"],
                           mac_address=port["mac_address"])

    with context.session.begin():
        db_api.port_delete(context, port)
Ejemplo n.º 11
0
def get_port_for_ip_address(context, ip_id, id, fields=None):
    """Retrieve a port.

    : param context: neutron api request context
    : param id: UUID representing the port to fetch.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_port %s for tenant %s fields %s" %
             (id, context.tenant_id, fields))
    addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE)
    if not addr:
        raise q_exc.IpAddressNotFound(addr_id=ip_id)

    filters = {'ip_address_id': [ip_id]}
    results = db_api.port_find(context,
                               id=id,
                               fields=fields,
                               scope=db_api.ONE,
                               **filters)

    if not results:
        raise n_exc.PortNotFound(port_id=id)

    return v._make_port_for_ip_dict(addr, results)
Ejemplo n.º 12
0
def get_ports(context, filters=None, fields=None):
    """Retrieve a list of ports.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictiontary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_ports for tenant %s filters %s fields %s" %
            (context.tenant_id, filters, fields))
    if filters is None:
        filters = {}
    query = db_api.port_find(context, fields=fields, **filters)
    return v._make_ports_list(query, fields)
Ejemplo n.º 13
0
def delete_port(context, id):
    """Delete a port.

    : param context: neutron api request context
    : param id: UUID representing the port to delete.
    """
    LOG.info("delete_port %s for tenant %s" % (id, context.tenant_id))

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

    if 'device_id' in port:  # false is weird, but ignore that
        LOG.info("delete_port %s for tenant %s has device %s" %
                 (id, context.tenant_id, port['device_id']))

    backend_key = port["backend_key"]
    mac_address = netaddr.EUI(port["mac_address"]).value
    ipam_driver = _get_ipam_driver(port["network"], port=port)
    ipam_driver.deallocate_mac_address(context, mac_address)
    ipam_driver.deallocate_ips_by_port(
        context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)

    net_driver = _get_net_driver(port["network"], port=port)
    base_net_driver = _get_net_driver(port["network"])
    net_driver.delete_port(context,
                           backend_key,
                           device_id=port["device_id"],
                           mac_address=port["mac_address"],
                           base_net_driver=base_net_driver)

    with context.session.begin():
        db_api.port_delete(context, port)
Ejemplo n.º 14
0
    def update_ports_for_sg(self, context, portid, jobid):
        """Updates the ports through redis."""
        port = db_api.port_find(context, id=portid, scope=db_api.ONE)
        if not port:
            LOG.warning("Port not found")
            return
        net_driver = port_api._get_net_driver(port.network, port=port)
        base_net_driver = port_api._get_net_driver(port.network)
        sg_list = [sg for sg in port.security_groups]

        success = False
        error = None
        retries = 3
        retry_delay = 2
        for retry in xrange(retries):
            try:
                net_driver.update_port(context, port_id=port["backend_key"],
                                       mac_address=port["mac_address"],
                                       device_id=port["device_id"],
                                       base_net_driver=base_net_driver,
                                       security_groups=sg_list)
                success = True
                error = None
                break
            except Exception as error:
                LOG.warning("Could not connect to redis, but retrying soon")
                time.sleep(retry_delay)
        status_str = ""
        if not success:
            status_str = "Port %s update failed after %d tries. Error: %s" % (
                portid, retries, error)
        update_body = dict(completed=True, status=status_str)
        update_body = dict(job=update_body)
        job_api.update_job(context.elevated(), jobid, update_body)
Ejemplo n.º 15
0
def update_port_for_ip_address(context, ip_id, id, port):
    """Update values of a port.

    : param context: neutron api request context
    : param ip_id: UUID representing the ip associated with port to update
    : param id: UUID representing the port to update.
    : param port: 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.
    """
    LOG.info("update_port %s for tenant %s" % (id, context.tenant_id))
    sanitize_list = ['service']
    with context.session.begin():
        addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE)
        if not addr:
            raise q_exc.IpAddressNotFound(addr_id=ip_id)
        port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
        if not port_db:
            raise q_exc.PortNotFound(port_id=id)
        port_dict = {k: port['port'][k] for k in sanitize_list}

        require_da = False
        service = port_dict.get('service')

        if require_da and _shared_ip_and_active(addr, except_port=id):
            raise q_exc.PortRequiresDisassociation()
        addr.set_service_for_port(port_db, service)
        context.session.add(addr)
    return v._make_port_for_ip_dict(addr, port_db)
Ejemplo n.º 16
0
def delete_port(context, id):
    """Delete a port.

    : param context: neutron api request context
    : param id: UUID representing the port to delete.
    """
    LOG.info("delete_port %s for tenant %s" % (id, context.tenant_id))

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

    if 'device_id' in port:  # false is weird, but ignore that
        LOG.info("delete_port %s for tenant %s has device %s" %
                 (id, context.tenant_id, port['device_id']))

    backend_key = port["backend_key"]
    mac_address = netaddr.EUI(port["mac_address"]).value
    ipam_driver = _get_ipam_driver(port["network"], port=port)
    ipam_driver.deallocate_mac_address(context, mac_address)
    ipam_driver.deallocate_ips_by_port(
        context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)

    net_driver = _get_net_driver(port["network"], port=port)
    base_net_driver = _get_net_driver(port["network"])
    net_driver.delete_port(context, backend_key, device_id=port["device_id"],
                           mac_address=port["mac_address"],
                           base_net_driver=base_net_driver)

    with context.session.begin():
        db_api.port_delete(context, port)
Ejemplo n.º 17
0
    def test_create_shared_ips_with_port_ids(self):

        def _make_body(ip):
            fix_ip = dict(ip_address=ip, subnet_id=sub['id'])
            port_info = {"port": dict(fixed_ips=[fix_ip])}
            return port_info

        with self._stubs(self.network, self.subnet, self.ports_info2) as (
                net, sub, ports):
            for p in ports:
                port_db = db_api.port_find(self.context, id=p['id'],
                                           scope=db_api.ONE)
                assocs = db_api.ip_port_association_find(self.context,
                                                         scope=db_api.ALL,
                                                         port_id=p['id'])
                self.assertEqual(1, len(p.get('fixed_ips')))
                self.assertEqual(1, len(port_db.ip_addresses))
                ip_db = port_db.ip_addresses[0]
                self.assertEqual('none', ip_db.get_service_for_port(port_db))
                self.assertEqual(1, len(assocs))

            port_ids = [ports[0]['id'], ports[1]['id']]
            shared_ip = {'ip_address': dict(port_ids=port_ids,
                                            network_id=net['id'],
                                            version=4)}
            ip = ip_api.create_ip_address(self.context, shared_ip)
            self.assertEqual(ip_types.SHARED, ip['type'])

            ports_ip = ip_api.get_ports_for_ip_address(self.context, ip['id'])
            self.assertEqual(2, len(ports_ip))
Ejemplo n.º 18
0
def get_ports(context, filters=None, fields=None):
    """Retrieve a list of ports.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictiontary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_ports for tenant %s filters %s fields %s" %
             (context.tenant_id, filters, fields))
    if filters is None:
        filters = {}
    query = db_api.port_find(context, fields=fields, **filters)
    return v._make_ports_list(query, fields)
Ejemplo n.º 19
0
def update_port_for_ip_address(context, ip_id, id, port):
    """Update values of a port.

    : param context: neutron api request context
    : param ip_id: UUID representing the ip associated with port to update
    : param id: UUID representing the port to update.
    : param port: 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.
    """
    LOG.info("update_port %s for tenant %s" % (id, context.tenant_id))
    sanitize_list = ['service']
    with context.session.begin():
        addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE)
        if not addr:
            raise q_exc.IpAddressNotFound(addr_id=ip_id)
        port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
        if not port_db:
            raise q_exc.PortNotFound(port_id=id)
        port_dict = {k: port['port'][k] for k in sanitize_list}

        require_da = False
        service = port_dict.get('service')

        if require_da and _shared_ip_and_active(addr, except_port=id):
            raise q_exc.PortRequiresDisassociation()
        addr.set_service_for_port(port_db, service)
        context.session.add(addr)
    return v._make_port_for_ip_dict(addr, port_db)
Ejemplo n.º 20
0
def _get_port(context, port_id):
    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 not port.ip_addresses or len(port.ip_addresses) == 0:
        raise q_exc.NoAvailableFixedIpsForPort(port_id=port_id)
    return port
Ejemplo n.º 21
0
def create_ip_address(context, ip_address):
    LOG.info("create_ip_address for tenant %s" % context.tenant_id)

    port = None
    ip_dict = ip_address["ip_address"]
    port_ids = ip_dict.get('port_ids')
    network_id = ip_dict.get('network_id')
    device_ids = ip_dict.get('device_ids')
    ip_version = ip_dict.get('version')
    ip_address = ip_dict.get('ip_address')

    ports = []
    if device_ids and not network_id:
        raise exceptions.BadRequest(
            resource="ip_addresses",
            msg="network_id is required if device_ids are supplied.")
    with context.session.begin():
        if network_id and device_ids:
            for device_id in device_ids:
                port = db_api.port_find(
                    context, network_id=network_id, device_id=device_id,
                    tenant_id=context.tenant_id, scope=db_api.ONE)
                ports.append(port)
        elif port_ids:
            for port_id in port_ids:
                port = db_api.port_find(context, id=port_id,
                                        tenant_id=context.tenant_id,
                                        scope=db_api.ONE)
                ports.append(port)

        if not ports:
            raise exceptions.PortNotFound(port_id=port_ids,
                                          net_id=network_id)

        address = ipam_driver.allocate_ip_address(
            context,
            port['network_id'],
            port['id'],
            CONF.QUARK.ipam_reuse_after,
            ip_version,
            ip_addresses=[ip_address])

        for port in ports:
            port["ip_addresses"].append(address)

    return v._make_ip_dict(address)
Ejemplo n.º 22
0
def _get_port(context, port_id):
    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 not port.ip_addresses or len(port.ip_addresses) == 0:
        raise q_exc.NoAvailableFixedIpsForPort(port_id=port_id)
    return port
Ejemplo n.º 23
0
def post_update_port(context, id, port):
    LOG.info("post_update_port %s for tenant %s" % (id, context.tenant_id))
    if not port.get("port"):
        raise exceptions.BadRequest(resource="ports", msg="Port body required")

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

    port = port["port"]
    if "fixed_ips" in port and port["fixed_ips"]:
        for ip in port["fixed_ips"]:
            address = None
            ipam_driver = ipam.IPAM_REGISTRY.get_strategy(
                port_db["network"]["ipam_strategy"])
            if ip:
                if "ip_id" in ip:
                    ip_id = ip["ip_id"]
                    address = db_api.ip_address_find(
                        context,
                        id=ip_id,
                        tenant_id=context.tenant_id,
                        scope=db_api.ONE)
                elif "ip_address" in ip:
                    ip_address = ip["ip_address"]
                    net_address = netaddr.IPAddress(ip_address)
                    address = db_api.ip_address_find(
                        context,
                        ip_address=net_address,
                        network_id=port_db["network_id"],
                        tenant_id=context.tenant_id,
                        scope=db_api.ONE)
                    if not address:
                        address = ipam_driver.allocate_ip_address(
                            context,
                            port_db["network_id"],
                            id,
                            CONF.QUARK.ipam_reuse_after,
                            ip_address=ip_address)
            else:
                address = ipam_driver.allocate_ip_address(
                    context, port_db["network_id"], id,
                    CONF.QUARK.ipam_reuse_after)

        address["deallocated"] = 0

        already_contained = False
        for port_address in port_db["ip_addresses"]:
            if address["id"] == port_address["id"]:
                already_contained = True
                break

        if not already_contained:
            port_db["ip_addresses"].append(address)
    return v._make_port_dict(port_db)
Ejemplo n.º 24
0
def get_ports(context,
              limit=None,
              sorts=['id'],
              marker=None,
              page_reverse=False,
              filters=None,
              fields=None):
    """Retrieve a list of ports.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_ports for tenant %s filters %s fields %s" %
             (context.tenant_id, filters, fields))
    if filters is None:
        filters = {}

    if "ip_address" in filters:
        if not context.is_admin:
            raise n_exc.NotAuthorized()
        ips = []
        try:
            ips = [netaddr.IPAddress(ip) for ip in filters.pop("ip_address")]
        except netaddr.AddrFormatError:
            raise n_exc.InvalidInput(
                error_message="Invalid format provided for ip_address")
        query = db_api.port_find_by_ip_address(context,
                                               ip_address=ips,
                                               scope=db_api.ALL,
                                               **filters)
        ports = []
        for ip in query:
            ports.extend(ip.ports)
    else:
        ports = db_api.port_find(context,
                                 limit,
                                 sorts,
                                 marker,
                                 fields=fields,
                                 join_security_groups=True,
                                 **filters)
    return v._make_ports_list(ports, fields)
Ejemplo n.º 25
0
def update_port(context, id, port):
    """Update values of a port.

    : param context: neutron api request context
    : param id: UUID representing the port to update.
    : param port: 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.
    """
    LOG.info("update_port %s for tenant %s" % (id, context.tenant_id))
    with context.session.begin():
        port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
        if not port_db:
            raise exceptions.PortNotFound(port_id=id)

        address_pairs = []
        fixed_ips = port["port"].pop("fixed_ips", None)
        if fixed_ips:
            ipam_driver = ipam.IPAM_REGISTRY.get_strategy(
                port_db["network"]["ipam_strategy"])
            ipam_driver.deallocate_ip_address(
                context, port_db, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)
            addresses = []
            for fixed_ip in fixed_ips:
                subnet_id = fixed_ip.get("subnet_id")
                ip_address = fixed_ip.get("ip_address")
                if not (subnet_id and ip_address):
                    raise exceptions.BadRequest(
                        resource="fixed_ips",
                        msg="subnet_id and ip_address required")
                # Note: we don't allow overlapping subnets, thus subnet_id is
                #       ignored.
                addresses.append(ipam_driver.allocate_ip_address(
                    context, port_db["network_id"], id,
                    CONF.QUARK.ipam_reuse_after, ip_address=ip_address))
            port["port"]["addresses"] = addresses
            mac_address_string = str(netaddr.EUI(port_db.mac_address,
                                                 dialect=netaddr.mac_unix))
            address_pairs = [{'mac_address': mac_address_string,
                              'ip_address':
                              address.get('address_readable', '')}
                             for address in addresses]

        group_ids, security_groups = v.make_security_group_list(
            context, port["port"].pop("security_groups", None))
        net_driver = registry.DRIVER_REGISTRY.get_driver(
            port_db.network["network_plugin"])
        net_driver.update_port(context, port_id=port_db.backend_key,
                               security_groups=group_ids,
                               allowed_pairs=address_pairs)

        port["port"]["security_groups"] = security_groups
        port = db_api.port_update(context, port_db, **port["port"])
    return v._make_port_dict(port)
Ejemplo n.º 26
0
    def post_update_port(self, context, id, port):
        LOG.info("post_update_port %s for tenant %s" % (id, context.tenant_id))
        port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
        if not port_db:
            raise exceptions.PortNotFound(port_id=id, net_id="")

        if "port" not in port or not port["port"]:
            raise exceptions.BadRequest()
        port = port["port"]

        if "fixed_ips" in port and port["fixed_ips"]:
            for ip in port["fixed_ips"]:
                address = None
                if ip:
                    if "ip_id" in ip:
                        ip_id = ip["ip_id"]
                        address = db_api.ip_address_find(
                            context,
                            id=ip_id,
                            tenant_id=context.tenant_id,
                            scope=db_api.ONE)
                    elif "ip_address" in ip:
                        ip_address = ip["ip_address"]
                        net_address = netaddr.IPAddress(ip_address)
                        address = db_api.ip_address_find(
                            context,
                            ip_address=net_address,
                            network_id=port_db["network_id"],
                            tenant_id=context.tenant_id,
                            scope=db_api.ONE)
                        if not address:
                            address = self.ipam_driver.allocate_ip_address(
                                context,
                                port_db["network_id"],
                                id,
                                self.ipam_reuse_after,
                                ip_address=ip_address)
                else:
                    address = self.ipam_driver.allocate_ip_address(
                        context,
                        port_db["network_id"],
                        id,
                        self.ipam_reuse_after)

            address["deallocated"] = 0

            already_contained = False
            for port_address in port_db["ip_addresses"]:
                if address["id"] == port_address["id"]:
                    already_contained = True
                    break

            if not already_contained:
                port_db["ip_addresses"].append(address)
        return self._make_port_dict(port_db)
Ejemplo n.º 27
0
def update_port(context, id, port):
    """Update values of a port.

    : param context: neutron api request context
    : param id: UUID representing the port to update.
    : param port: 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.
    """
    LOG.info("update_port %s for tenant %s" % (id, context.tenant_id))
    port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
    if not port_db:
        raise exceptions.PortNotFound(port_id=id)

    address_pairs = []
    fixed_ips = port["port"].pop("fixed_ips", None)
    if fixed_ips:
        ipam_driver.deallocate_ip_address(
            context, port_db, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)
        addresses = []
        for fixed_ip in fixed_ips:
            subnet_id = fixed_ip.get("subnet_id")
            ip_address = fixed_ip.get("ip_address")
            if not (subnet_id and ip_address):
                raise exceptions.BadRequest(
                    resource="fixed_ips",
                    msg="subnet_id and ip_address required")
            # Note: we don't allow overlapping subnets, thus subnet_id is
            #       ignored.
            addresses.append(
                ipam_driver.allocate_ip_address(context,
                                                port_db["network_id"],
                                                id,
                                                CONF.QUARK.ipam_reuse_after,
                                                ip_address=ip_address))
        port["port"]["addresses"] = addresses
        mac_address_string = str(
            netaddr.EUI(port_db.mac_address, dialect=netaddr.mac_unix))
        address_pairs = [{
            'mac_address': mac_address_string,
            'ip_address': address.get('address_readable', '')
        } for address in addresses]

    group_ids, security_groups = v.make_security_group_list(
        context, port["port"].pop("security_groups", None))
    net_driver.update_port(context,
                           port_id=port_db.backend_key,
                           security_groups=group_ids,
                           allowed_pairs=address_pairs)

    port["port"]["security_groups"] = security_groups
    port = db_api.port_update(context, port_db, **port["port"])
    return v._make_port_dict(port)
Ejemplo n.º 28
0
def post_update_port(context, id, port):
    LOG.info("post_update_port %s for tenant %s" % (id, context.tenant_id))
    if not port.get("port"):
        raise exceptions.BadRequest(resource="ports", msg="Port body required")

    with context.session.begin():
        port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
        if not port_db:
            raise exceptions.PortNotFound(port_id=id, net_id="")

        port = port["port"]
        if "fixed_ips" in port and port["fixed_ips"]:
            for ip in port["fixed_ips"]:
                address = None
                ipam_driver = ipam.IPAM_REGISTRY.get_strategy(port_db["network"]["ipam_strategy"])
                if ip:
                    if "ip_id" in ip:
                        ip_id = ip["ip_id"]
                        address = db_api.ip_address_find(
                            context, id=ip_id, tenant_id=context.tenant_id, scope=db_api.ONE
                        )
                    elif "ip_address" in ip:
                        ip_address = ip["ip_address"]
                        net_address = netaddr.IPAddress(ip_address)
                        address = db_api.ip_address_find(
                            context,
                            ip_address=net_address,
                            network_id=port_db["network_id"],
                            tenant_id=context.tenant_id,
                            scope=db_api.ONE,
                        )
                        if not address:
                            address = ipam_driver.allocate_ip_address(
                                context, port_db["network_id"], id, CONF.QUARK.ipam_reuse_after, ip_address=ip_address
                            )
                else:
                    address = ipam_driver.allocate_ip_address(
                        context, port_db["network_id"], id, CONF.QUARK.ipam_reuse_after
                    )

            address["deallocated"] = 0

            already_contained = False
            for port_address in port_db["ip_addresses"]:
                if address["id"] == port_address["id"]:
                    already_contained = True
                    break

            if not already_contained:
                port_db["ip_addresses"].append(address)
    return v._make_port_dict(port_db)
Ejemplo n.º 29
0
def update_ip_address(context, id, ip_address):
    LOG.info("update_ip_address %s for tenant %s" %
             (id, context.tenant_id))
    ports = []
    with context.session.begin():
        address = db_api.ip_address_find(
            context, id=id, tenant_id=context.tenant_id, scope=db_api.ONE)
        if not address:
            raise exceptions.NotFound(
                message="No IP address found with id=%s" % id)

        reset = ip_address['ip_address'].get('reset_allocation_time', False)
        if reset and address['deallocated'] == 1:
            if context.is_admin:
                LOG.info("IP's deallocated time being manually reset")
                address['deallocated_at'] = _get_deallocated_override()
            else:
                msg = "Modification of reset_allocation_time requires admin"
                raise webob.exc.HTTPForbidden(detail=msg)

        port_ids = ip_address['ip_address'].get('port_ids')

        if port_ids:
            _raise_if_shared_and_enabled(ip_address, address)
            ports = db_api.port_find(context, tenant_id=context.tenant_id,
                                     id=port_ids, scope=db_api.ALL)
            # NOTE(name): could be considered inefficient because we're
            # converting to a list to check length. Maybe revisit
            if len(ports) != len(port_ids):
                raise exceptions.NotFound(
                    message="No ports not found with ids=%s" % port_ids)

            validate_ports_on_network_and_same_segment(ports,
                                                       address["network_id"])
            validate_port_ip_quotas(context, ports)

            LOG.info("Updating IP address, %s, to only be used by the"
                     "following ports:  %s" % (address.address_readable,
                                               [p.id for p in ports]))
            new_address = db_api.update_port_associations_for_ip(context,
                                                                 ports,
                                                                 address)
        else:
            if port_ids is not None:
                ipam_driver.deallocate_ip_address(
                    context, address)
            return v._make_ip_dict(address)
    return v._make_ip_dict(new_address)
Ejemplo n.º 30
0
def update_ip_address(context, id, ip_address):
    LOG.info("update_ip_address %s for tenant %s" % (id, context.tenant_id))

    with context.session.begin():
        address = db_api.ip_address_find(context,
                                         id=id,
                                         tenant_id=context.tenant_id,
                                         scope=db_api.ONE)

        if not address:
            raise exceptions.NotFound(
                message="No IP address found with id=%s" % id)

        reset = ip_address['ip_address'].get('reset_allocation_time', False)
        if reset and address['deallocated'] == 1:
            if context.is_admin:
                LOG.info("IP's deallocated time being manually reset")
                address['deallocated_at'] = _get_deallocated_override()
            else:
                msg = "Modification of reset_allocation_time requires admin"
                raise webob.exc.HTTPForbidden(detail=msg)

        old_ports = address['ports']
        port_ids = ip_address['ip_address'].get('port_ids')
        if port_ids is None:
            return v._make_ip_dict(address)

        for port in old_ports:
            port['ip_addresses'].remove(address)

        if port_ids:
            ports = db_api.port_find(context,
                                     tenant_id=context.tenant_id,
                                     id=port_ids,
                                     scope=db_api.ALL)

            # NOTE: could be considered inefficient because we're converting
            #       to a list to check length. Maybe revisit
            if len(ports) != len(port_ids):
                raise exceptions.NotFound(
                    message="No ports not found with ids=%s" % port_ids)
            for port in ports:
                port['ip_addresses'].extend([address])
        else:
            address["deallocated"] = 1

    return v._make_ip_dict(address)
Ejemplo n.º 31
0
def get_port(context, id, fields=None):
    """Retrieve a port.

    : param context: neutron api request context
    : param id: UUID representing the port to fetch.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_port %s for tenant %s fields %s" % (id, context.tenant_id, fields))
    results = db_api.port_find(context, id=id, fields=fields, scope=db_api.ONE)

    if not results:
        raise exceptions.PortNotFound(port_id=id, net_id="")

    return v._make_port_dict(results)
Ejemplo n.º 32
0
def update_ip_address(context, id, ip_address):
    LOG.info("update_ip_address %s for tenant %s" %
             (id, context.tenant_id))

    with context.session.begin():
        address = db_api.ip_address_find(
            context, id=id, tenant_id=context.tenant_id, scope=db_api.ONE)

        if not address:
            raise exceptions.NotFound(
                message="No IP address found with id=%s" % id)

        reset = ip_address['ip_address'].get('reset_allocation_time',
                                             False)
        if reset and address['deallocated'] == 1:
            if context.is_admin:
                LOG.info("IP's deallocated time being manually reset")
                address['deallocated_at'] = _get_deallocated_override()
            else:
                msg = "Modification of reset_allocation_time requires admin"
                raise webob.exc.HTTPForbidden(detail=msg)

        old_ports = address['ports']
        port_ids = ip_address['ip_address'].get('port_ids')
        if port_ids is None:
            return v._make_ip_dict(address)

        for port in old_ports:
            port['ip_addresses'].remove(address)

        if port_ids:
            ports = db_api.port_find(
                context, tenant_id=context.tenant_id, id=port_ids,
                scope=db_api.ALL)

            # NOTE: could be considered inefficient because we're converting
            #       to a list to check length. Maybe revisit
            if len(ports) != len(port_ids):
                raise exceptions.NotFound(
                    message="No ports not found with ids=%s" % port_ids)
            for port in ports:
                port['ip_addresses'].extend([address])
        else:
            address["deallocated"] = 1

    return v._make_ip_dict(address)
Ejemplo n.º 33
0
 def test_ports_sorted_by_created_at(self):
     # create a network
     network = dict(name="public", tenant_id="fake", network_plugin="BASE")
     net_mod = db_api.network_create(self.context, **network)
     # create ports
     port1 = dict(network_id=net_mod["id"], backend_key="1", device_id="1")
     port2 = dict(network_id=net_mod["id"], backend_key="1", device_id="1")
     port3 = dict(network_id=net_mod["id"], backend_key="1", device_id="1")
     port_mod1 = db_api.port_create(self.context, **port1)
     port_mod2 = db_api.port_create(self.context, **port2)
     port_mod3 = db_api.port_create(self.context, **port3)
     res = db_api.port_find(self.context, scope=db_api.ALL)
     self.assertTrue(res[0]["created_at"] < res[1]["created_at"] < res[2]["created_at"])
     db_api.network_delete(self.context, net_mod)
     db_api.port_delete(self.context, port_mod1)
     db_api.port_delete(self.context, port_mod2)
     db_api.port_delete(self.context, port_mod3)
Ejemplo n.º 34
0
def get_ports_for_ip_address(context,
                             ip_id,
                             limit=None,
                             sorts=None,
                             marker=None,
                             page_reverse=False,
                             filters=None,
                             fields=None):
    """Retrieve a list of ports.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_ports for tenant %s filters %s fields %s" %
             (context.tenant_id, filters, fields))
    addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE)
    if not addr:
        raise q_exc.IpAddressNotFound(addr_id=ip_id)

    if filters is None:
        filters = {}

    filters['ip_address_id'] = [ip_id]

    ports = db_api.port_find(context,
                             limit,
                             sorts,
                             marker,
                             fields=fields,
                             join_security_groups=True,
                             **filters)
    return v._make_ip_ports_list(addr, ports, fields)
Ejemplo n.º 35
0
    def test_shared_ip_in_fixed_ip_list(self):

        def _make_body(service):
            body = dict(service=service)
            port_info = {"port": dict(body)}
            return port_info

        with self._stubs(self.network, self.subnet, self.ports_info2) as (
                net, sub, ports):
            for p in ports:
                port_db = db_api.port_find(self.context, id=p['id'],
                                           scope=db_api.ONE)
                assocs = db_api.ip_port_association_find(self.context,
                                                         scope=db_api.ALL,
                                                         port_id=p['id'])
                self.assertEqual(1, len(p.get('fixed_ips')))
                self.assertEqual(1, len(port_db.ip_addresses))
                ip_db = port_db.ip_addresses[0]
                self.assertEqual('none', ip_db.get_service_for_port(port_db))
                self.assertEqual(1, len(assocs))

            device_ids = [ports[0]['device_id'], ports[1]['device_id']]
            shared_ip = {'ip_address': dict(device_ids=device_ids,
                                            network_id=net['id'],
                                            version=4)}
            ip = ip_api.create_ip_address(self.context, shared_ip)
            self.assertEqual(ip_types.SHARED, ip['type'])

            ports_ip = ip_api.get_ports_for_ip_address(self.context, ip['id'])
            self.assertEqual(2, len(ports_ip))

            port = port_api.get_port(self.context, ports[0]['id'])
            self.assertEqual(2, len(port['fixed_ips']))

            port_ip_update = ip_api.update_port_for_ip_address
            updated_port = port_ip_update(self.context, ip['id'],
                                          ports[0]['id'], _make_body('derp'))
            self.assertEqual('derp', updated_port.get('service'))

            port = ip_api.get_port_for_ip_address(self.context, ip['id'],
                                                  ports[0]['id'])
            self.assertEqual('derp', port.get('service'))
            port = ip_api.get_port_for_ip_address(self.context, ip['id'],
                                                  ports[1]['id'])
            self.assertEqual('none', port.get('service'))
Ejemplo n.º 36
0
def get_port(context, id, fields=None):
    """Retrieve a port.

    : param context: neutron api request context
    : param id: UUID representing the port to fetch.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_port %s for tenant %s fields %s" %
             (id, context.tenant_id, fields))
    results = db_api.port_find(context, id=id, fields=fields, scope=db_api.ONE)

    if not results:
        raise exceptions.PortNotFound(port_id=id, net_id='')

    return v._make_port_dict(results)
Ejemplo n.º 37
0
def get_ports(context, limit=None, sorts=None, marker=None, page_reverse=False,
              filters=None, fields=None):
    """Retrieve a list of ports.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_ports for tenant %s filters %s fields %s" %
             (context.tenant_id, filters, fields))
    if filters is None:
        filters = {}

    if "ip_address" in filters:
        if not context.is_admin:
            raise n_exc.NotAuthorized()
        ips = []
        try:
            ips = [netaddr.IPAddress(ip) for ip in filters.pop("ip_address")]
        except netaddr.AddrFormatError:
            raise n_exc.InvalidInput(
                error_message="Invalid format provided for ip_address")
        query = db_api.port_find_by_ip_address(context, ip_address=ips,
                                               scope=db_api.ALL, **filters)
        ports = []
        for ip in query:
            ports.extend(ip.ports)
    else:
        ports = db_api.port_find(context, limit, sorts, marker,
                                 fields=fields, join_security_groups=True,
                                 **filters)
    return v._make_ports_list(ports, fields)
Ejemplo n.º 38
0
def delete_port(context, id):
    """Delete a port.

    : param context: neutron api request context
    : param id: UUID representing the port to delete.
    """
    LOG.info("delete_port %s for tenant %s" % (id, context.tenant_id))

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

    backend_key = port["backend_key"]
    mac_address = netaddr.EUI(port["mac_address"]).value
    ipam_driver.deallocate_mac_address(context, mac_address)
    ipam_driver.deallocate_ip_address(
        context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)
    db_api.port_delete(context, port)
    net_driver.delete_port(context, backend_key)
Ejemplo n.º 39
0
    def disassociate_port(self, context, id, ip_address_id):
        """Disassociates a port from an IP address.

        : param context: quantum api request context
        : param id: UUID representing the port to disassociate.
        : param ip_address_id: UUID representing the IP address to
        disassociate.
        """
        LOG.info("disassociate_port %s for tenant %s ip_address_id %s" %
                (id, context.tenant_id, ip_address_id))
        port = db_api.port_find(context, id=id, ip_address_id=[ip_address_id],
                                scope=db_api.ONE)

        if not port:
            raise exceptions.PortNotFound(port_id=id, net_id='')

        port["ip_addresses"] = [address for address in port["ip_addresses"]
                                if address.id != ip_address_id]

        return self._make_port_dict(port)
Ejemplo n.º 40
0
def delete_port(context, id):
    """Delete a port.

    : param context: neutron api request context
    : param id: UUID representing the port to delete.
    """
    LOG.info("delete_port %s for tenant %s" %
            (id, context.tenant_id))

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

    backend_key = port["backend_key"]
    mac_address = netaddr.EUI(port["mac_address"]).value
    ipam_driver.deallocate_mac_address(context, mac_address)
    ipam_driver.deallocate_ip_address(
        context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after)
    db_api.port_delete(context, port)
    net_driver.delete_port(context, backend_key)
Ejemplo n.º 41
0
def get_ports_for_ip_address(context, ip_id, limit=None, sorts=None,
                             marker=None, page_reverse=False, filters=None,
                             fields=None):
    """Retrieve a list of ports.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_ports for tenant %s filters %s fields %s" %
             (context.tenant_id, filters, fields))
    addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE)
    if not addr:
        raise q_exc.IpAddressNotFound(addr_id=ip_id)

    if filters is None:
        filters = {}

    filters['ip_address_id'] = [ip_id]

    ports = db_api.port_find(context, limit, sorts, marker,
                             fields=fields, join_security_groups=True,
                             **filters)
    return v._make_ip_ports_list(addr, ports, fields)
Ejemplo n.º 42
0
def get_port_for_ip_address(context, ip_id, id, fields=None):
    """Retrieve a port.

    : param context: neutron api request context
    : param id: UUID representing the port to fetch.
    : param fields: a list of strings that are valid keys in a
        port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_port %s for tenant %s fields %s" %
             (id, context.tenant_id, fields))
    addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE)
    if not addr:
        raise q_exc.IpAddressNotFound(addr_id=ip_id)

    filters = {'ip_address_id': [ip_id]}
    results = db_api.port_find(context, id=id, fields=fields,
                               scope=db_api.ONE, **filters)

    if not results:
        raise n_exc.PortNotFound(port_id=id)

    return v._make_port_for_ip_dict(addr, results)
Ejemplo n.º 43
0
    def update_port(self, context, id, port):
        """Update values of a port.

        : param context: quantum api request context
        : param id: UUID representing the port to update.
        : param port: 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
            quantum/api/v2/attributes.py.
        """
        LOG.info("update_port %s for tenant %s" % (id, context.tenant_id))
        port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
        if not port_db:
            raise exceptions.PortNotFound(port_id=id)

        fixed_ips = port["port"].pop("fixed_ips", None)
        if fixed_ips:
            addresses = []
            for fixed_ip in fixed_ips:
                subnet_id = fixed_ip.get("subnet_id")
                ip_address = fixed_ip.get("ip_address")
                if not (subnet_id and ip_address):
                    raise exceptions.BadRequest(
                        resource="fixed_ips",
                        msg="subnet_id and ip_address required")
                # Note: we don't allow overlapping subnets, thus subnet_id is
                #       ignored.
                addresses.append(self.ipam_driver.allocate_ip_address(
                    context, port_db["network_id"], id,
                    self.ipam_reuse_after, ip_address=ip_address))
            port["port"]["addresses"] = addresses

        port = db_api.port_update(context,
                                  port_db,
                                  **port["port"])
        return self._make_port_dict(port)
Ejemplo n.º 44
0
 def test_port_find_ip_address_id(self):
     self.context.session.query = mock.Mock()
     db_api.port_find(self.context, ip_address_id="fake")
     query_obj = self.context.session.query.return_value
     filter_fn = query_obj.options.return_value.filter
     self.assertEqual(filter_fn.call_count, 1)
Ejemplo n.º 45
0
def create_floatingip(context, content):
    LOG.info("create_floatingip %s for tenant %s and body %s" %
             (id, context.tenant_id, content))
    tenant_id = content.get("tenant_id")
    network_id = content.get("floating_network_id")
    fixed_ip_address = content.get("fixed_ip_address")
    ip_address = content.get("floating_ip_address")
    port_id = content.get("port_id")

    if not tenant_id:
        tenant_id = context.tenant_id

    if not network_id:
        raise exceptions.BadRequest(resource="floating_ip",
                                    msg="floating_network_id is required.")

    network = db_api.network_find(context, id=network_id, scope=db_api.ONE)

    if not network:
        raise exceptions.NetworkNotFound(net_id=network_id)

    fixed_ip = None
    port = None
    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 not port.ip_addresses or len(port.ip_addresses) == 0:
            raise quark_exceptions.NoAvailableFixedIPsForPort(port_id=port_id)

        if not fixed_ip_address:
            fixed_ip = _get_next_available_fixed_ip(port)
            if not fixed_ip:
                raise quark_exceptions.NoAvailableFixedIPsForPort(
                    port_id=port_id)
        else:
            fixed_ip = next(
                (ip for ip in port.ip_addresses
                 if (ip["address_readable"] == fixed_ip_address
                     and ip.get("address_type") == ip_types.FIXED)), None)

            if not fixed_ip:
                raise quark_exceptions.FixedIpDoesNotExistsForPort(
                    fixed_ip=fixed_ip_address, port_id=port_id)

            if any(ip for ip in port.ip_addresses
                   if (ip.get("address_type") == ip_types.FLOATING and
                       ip.fixed_ip["address_readable"] == fixed_ip_address)):
                raise quark_exceptions.PortAlreadyContainsFloatingIp(
                    port_id=port_id)

    new_addresses = []
    ip_addresses = []
    if ip_address:
        ip_addresses.append(ip_address)

    seg_name = CONF.QUARK.floating_ip_segment_name
    strategy_name = network.get("ipam_strategy")
    ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
    ipam_driver.allocate_ip_address(context,
                                    new_addresses,
                                    network_id,
                                    port_id,
                                    CONF.QUARK.ipam_reuse_after,
                                    seg_name,
                                    version=4,
                                    ip_addresses=ip_addresses,
                                    address_type=ip_types.FLOATING)

    floating_ip = new_addresses[0]

    if fixed_ip and port:
        with context.session.begin():
            floating_ip = db_api.floating_ip_associate_fixed_ip(
                context, floating_ip, fixed_ip)

        flip_driver_type = CONF.QUARK.default_floating_ip_driver
        flip_driver = registry.DRIVER_REGISTRY.get_driver(flip_driver_type)

        flip_driver.register_floating_ip(floating_ip, port, fixed_ip)

    return v._make_floating_ip_dict(floating_ip)
Ejemplo n.º 46
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
Ejemplo n.º 47
0
def update_ip_address(context, id, ip_address):
    """Due to NCP-1592 ensure that address_type cannot change after update."""
    LOG.info("update_ip_address %s for tenant %s" % (id, context.tenant_id))
    ports = []
    if 'ip_address' not in ip_address:
        raise n_exc.BadRequest(resource="ip_addresses",
                               msg="Invalid request body.")
    with context.session.begin():
        address = db_api.ip_address_find(context, id=id, scope=db_api.ONE)
        if not address:
            raise q_exc.IpAddressNotFound(addr_id=id)
        iptype = address.address_type
        if iptype == ip_types.FIXED and not CONF.QUARK.ipaddr_allow_fixed_ip:
            raise n_exc.BadRequest(
                resource="ip_addresses",
                msg="Fixed ips cannot be updated using this interface.")

        reset = ip_address['ip_address'].get('reset_allocation_time', False)
        if reset and address['deallocated'] == 1:
            if context.is_admin:
                LOG.info("IP's deallocated time being manually reset")
                address['deallocated_at'] = _get_deallocated_override()
            else:
                msg = "Modification of reset_allocation_time requires admin"
                raise webob.exc.HTTPForbidden(detail=msg)

        port_ids = ip_address['ip_address'].get('port_ids', None)

        if port_ids is not None and not port_ids:
            raise n_exc.BadRequest(
                resource="ip_addresses",
                msg="Cannot be updated with empty port_id list")

        if iptype == ip_types.SHARED:
            has_owner = address.has_any_shared_owner()

        if port_ids:
            if iptype == ip_types.FIXED and len(port_ids) > 1:
                raise n_exc.BadRequest(
                    resource="ip_addresses",
                    msg="Fixed ips cannot be updated with more than one port.")

            _raise_if_shared_and_enabled(ip_address, address)
            ports = db_api.port_find(context,
                                     tenant_id=context.tenant_id,
                                     id=port_ids,
                                     scope=db_api.ALL)
            # NOTE(name): could be considered inefficient because we're
            # converting to a list to check length. Maybe revisit
            if len(ports) != len(port_ids):
                raise n_exc.PortNotFound(port_id=port_ids)

            validate_and_fetch_segment(ports, address["network_id"])
            validate_port_ip_quotas(context, address.network_id, ports)

            if iptype == ip_types.SHARED and has_owner:
                for assoc in address.associations:
                    pid = assoc.port_id
                    if pid not in port_ids and 'none' != assoc.service:
                        raise q_exc.PortRequiresDisassociation()

            LOG.info("Updating IP address, %s, to only be used by the"
                     "following ports:  %s" %
                     (address.address_readable, [p.id for p in ports]))
            new_address = db_api.update_port_associations_for_ip(
                context, ports, address)
        elif iptype == ip_types.SHARED and has_owner:
            raise q_exc.PortRequiresDisassociation()
        else:
            ipam_driver.deallocate_ip_address(context, address)
            return v._make_ip_dict(address)
    return v._make_ip_dict(new_address)
Ejemplo n.º 48
0
def create_port(context, port):
    """Create a port

    Create a port which is a connection point of a device (e.g., a VM
    NIC) to attach to a L2 Neutron network.
    : param context: neutron api request context
    : param port: dictionary describing the port, with keys
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.  All keys will be populated.
    """
    LOG.info("create_port for tenant %s" % context.tenant_id)
    port_attrs = port["port"]

    admin_only = [
        "mac_address", "device_owner", "bridge", "admin_state_up",
        "use_forbidden_mac_range", "network_plugin", "instance_node_id"
    ]
    utils.filter_body(context, port_attrs, admin_only=admin_only)

    port_attrs = port["port"]
    mac_address = utils.pop_param(port_attrs, "mac_address", None)
    use_forbidden_mac_range = utils.pop_param(port_attrs,
                                              "use_forbidden_mac_range", False)
    segment_id = utils.pop_param(port_attrs, "segment_id")
    fixed_ips = utils.pop_param(port_attrs, "fixed_ips")

    if "device_id" not in port_attrs:
        port_attrs['device_id'] = ""
    device_id = port_attrs['device_id']

    # NOTE(morgabra) This should be instance.node from nova, only needed
    # for ironic_driver.
    if "instance_node_id" not in port_attrs:
        port_attrs['instance_node_id'] = ""
    instance_node_id = port_attrs['instance_node_id']

    net_id = port_attrs["network_id"]

    port_id = uuidutils.generate_uuid()

    net = db_api.network_find(context=context,
                              limit=None,
                              sorts=['id'],
                              marker=None,
                              page_reverse=False,
                              fields=None,
                              id=net_id,
                              scope=db_api.ONE)

    if not net:
        raise n_exc.NetworkNotFound(net_id=net_id)
    _raise_if_unauthorized(context, net)

    # NOTE (Perkins): If a device_id is given, try to prevent multiple ports
    # from being created for a device already attached to the network
    if device_id:
        existing_ports = db_api.port_find(context,
                                          network_id=net_id,
                                          device_id=device_id,
                                          scope=db_api.ONE)
        if existing_ports:
            raise n_exc.BadRequest(
                resource="port",
                msg="This device is already connected to the "
                "requested network via another port")

    # Try to fail early on quotas and save ourselves some db overhead
    if fixed_ips:
        quota.QUOTAS.limit_check(context,
                                 context.tenant_id,
                                 fixed_ips_per_port=len(fixed_ips))

    if not STRATEGY.is_provider_network(net_id):
        # We don't honor segmented networks when they aren't "shared"
        segment_id = None
        port_count = db_api.port_count_all(context,
                                           network_id=[net_id],
                                           tenant_id=[context.tenant_id])
        quota.QUOTAS.limit_check(context,
                                 context.tenant_id,
                                 ports_per_network=port_count + 1)
    else:
        if not segment_id:
            raise q_exc.AmbiguousNetworkId(net_id=net_id)

    network_plugin = utils.pop_param(port_attrs, "network_plugin")
    if not network_plugin:
        network_plugin = net["network_plugin"]
    port_attrs["network_plugin"] = network_plugin

    ipam_driver = _get_ipam_driver(net, port=port_attrs)
    net_driver = _get_net_driver(net, port=port_attrs)
    # NOTE(morgabra) It's possible that we select a driver different than
    # the one specified by the network. However, we still might need to use
    # this for some operations, so we also fetch it and pass it along to
    # the backend driver we are actually using.
    base_net_driver = _get_net_driver(net)

    # TODO(anyone): security groups are not currently supported on port create.
    #               Please see JIRA:NCP-801
    security_groups = utils.pop_param(port_attrs, "security_groups")
    if security_groups is not None:
        raise q_exc.SecurityGroupsNotImplemented()

    group_ids, security_groups = _make_security_group_list(
        context, security_groups)
    quota.QUOTAS.limit_check(context,
                             context.tenant_id,
                             security_groups_per_port=len(group_ids))
    addresses = []
    backend_port = None

    with utils.CommandManager().execute() as cmd_mgr:

        @cmd_mgr.do
        def _allocate_ips(fixed_ips, net, port_id, segment_id, mac, **kwargs):
            if fixed_ips:
                if (STRATEGY.is_provider_network(net_id)
                        and not context.is_admin):
                    raise n_exc.NotAuthorized()

                ips, subnets = split_and_validate_requested_subnets(
                    context, net_id, segment_id, fixed_ips)
                kwargs["ip_addresses"] = ips
                kwargs["subnets"] = subnets

            ipam_driver.allocate_ip_address(context,
                                            addresses,
                                            net["id"],
                                            port_id,
                                            CONF.QUARK.ipam_reuse_after,
                                            segment_id=segment_id,
                                            mac_address=mac,
                                            **kwargs)

        @cmd_mgr.undo
        def _allocate_ips_undo(addr, **kwargs):
            LOG.info("Rolling back IP addresses...")
            if addresses:
                for address in addresses:
                    try:
                        with context.session.begin():
                            ipam_driver.deallocate_ip_address(
                                context, address, **kwargs)
                    except Exception:
                        LOG.exception("Couldn't release IP %s" % address)

        @cmd_mgr.do
        def _allocate_mac(net,
                          port_id,
                          mac_address,
                          use_forbidden_mac_range=False,
                          **kwargs):
            mac = ipam_driver.allocate_mac_address(
                context,
                net["id"],
                port_id,
                CONF.QUARK.ipam_reuse_after,
                mac_address=mac_address,
                use_forbidden_mac_range=use_forbidden_mac_range,
                **kwargs)
            return mac

        @cmd_mgr.undo
        def _allocate_mac_undo(mac, **kwargs):
            LOG.info("Rolling back MAC address...")
            if mac:
                try:
                    with context.session.begin():
                        ipam_driver.deallocate_mac_address(
                            context, mac["address"])
                except Exception:
                    LOG.exception("Couldn't release MAC %s" % mac)

        @cmd_mgr.do
        def _allocate_backend_port(mac, addresses, net, port_id, **kwargs):
            backend_port = net_driver.create_port(
                context,
                net["id"],
                port_id=port_id,
                security_groups=group_ids,
                device_id=device_id,
                instance_node_id=instance_node_id,
                mac_address=mac,
                addresses=addresses,
                base_net_driver=base_net_driver)
            _filter_backend_port(backend_port)
            return backend_port

        @cmd_mgr.undo
        def _allocate_back_port_undo(backend_port, **kwargs):
            LOG.info("Rolling back backend port...")
            try:
                backend_port_uuid = None
                if backend_port:
                    backend_port_uuid = backend_port.get("uuid")
                net_driver.delete_port(context, backend_port_uuid)
            except Exception:
                LOG.exception("Couldn't rollback backend port %s" %
                              backend_port)

        @cmd_mgr.do
        def _allocate_db_port(port_attrs, backend_port, addresses, mac,
                              **kwargs):
            port_attrs["network_id"] = net["id"]
            port_attrs["id"] = port_id
            port_attrs["security_groups"] = security_groups

            LOG.info("Including extra plugin attrs: %s" % backend_port)
            port_attrs.update(backend_port)
            with context.session.begin():
                new_port = db_api.port_create(context,
                                              addresses=addresses,
                                              mac_address=mac["address"],
                                              backend_key=backend_port["uuid"],
                                              **port_attrs)

            return new_port

        @cmd_mgr.undo
        def _allocate_db_port_undo(new_port, **kwargs):
            LOG.info("Rolling back database port...")
            if not new_port:
                return
            try:
                with context.session.begin():
                    db_api.port_delete(context, new_port)
            except Exception:
                LOG.exception("Couldn't rollback db port %s" % backend_port)

        # addresses, mac, backend_port, new_port
        mac = _allocate_mac(net,
                            port_id,
                            mac_address,
                            use_forbidden_mac_range=use_forbidden_mac_range)
        _allocate_ips(fixed_ips, net, port_id, segment_id, mac)
        backend_port = _allocate_backend_port(mac, addresses, net, port_id)
        new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac)

    return v._make_port_dict(new_port)
Ejemplo n.º 49
0
def create_ip_address(context, body):
    LOG.info("create_ip_address for tenant %s" % context.tenant_id)
    iptype = (ip_types.SHARED if _shared_ip_request(body) else ip_types.FIXED)
    if 'ip_address' not in body:
        raise n_exc.BadRequest(resource="ip_addresses",
                               msg="Invalid request body.")
    if iptype == ip_types.FIXED and not CONF.QUARK.ipaddr_allow_fixed_ip:
        raise n_exc.BadRequest(resource="ip_addresses",
                               msg="Only shared IPs may be made with "
                               "this resource.")
    ip_dict = body.get("ip_address")
    port_ids = ip_dict.get('port_ids', [])
    network_id = ip_dict.get('network_id')
    device_ids = ip_dict.get('device_ids')
    ip_version = ip_dict.get('version')
    ip_address = ip_dict.get('ip_address')
    # If no version is passed, you would get what the network provides,
    # which could be both v4 and v6 addresses. Rather than allow for such
    # an ambiguous outcome, we'll raise instead
    if not ip_version:
        raise n_exc.BadRequest(resource="ip_addresses",
                               msg="version is required.")
    if network_id is None:
        raise n_exc.BadRequest(resource="ip_addresses",
                               msg="network_id is required.")
    if network_id == "":
        raise n_exc.NetworkNotFound(net_id=network_id)
    net = db_api.network_find(context,
                              None,
                              None,
                              None,
                              False,
                              id=network_id,
                              scope=db_api.ONE)
    if not net:
        raise n_exc.NetworkNotFound(net_id=network_id)
    if not port_ids and not device_ids:
        raise n_exc.BadRequest(resource="ip_addresses",
                               msg="port_ids or device_ids required.")

    new_addresses = []
    ports = []
    by_device = False
    with context.session.begin():
        if network_id and device_ids:
            by_device = True
            for device_id in device_ids:
                port = db_api.port_find(context,
                                        network_id=network_id,
                                        device_id=device_id,
                                        tenant_id=context.tenant_id,
                                        scope=db_api.ONE)
                if port is not None:
                    ports.append(port)
        elif port_ids:
            for port_id in port_ids:

                port = db_api.port_find(context,
                                        id=port_id,
                                        tenant_id=context.tenant_id,
                                        scope=db_api.ONE)
                if port is not None:
                    ports.append(port)

        if not ports:
            raise n_exc.PortNotFoundOnNetwork(port_id=port_ids,
                                              net_id=network_id)

    if ((by_device and len(device_ids) != len(ports))
            or (not by_device and len(port_ids) != len(ports))):
        raise q_exc.NotAllPortOrDeviceFound()

    segment_id = validate_and_fetch_segment(ports, network_id)
    if iptype == ip_types.SHARED:
        old_addresses = db_api.ip_address_find(context,
                                               network_id=network_id,
                                               address_type=ip_types.SHARED,
                                               scope=db_api.ALL)
        validate_shared_ips_quotas(context, network_id, old_addresses)
    validate_port_ip_quotas(context, network_id, ports)

    # Shared Ips are only new IPs. Two use cases: if we got device_id
    # or if we got port_ids. We should check the case where we got port_ids
    # and device_ids. The device_id must have a port on the network,
    # and any port_ids must also be on that network already. If we have
    # more than one port by this step, it's considered a shared IP,
    # and therefore will be marked as unconfigured (enabled=False)
    # for all ports.
    ipam_driver.allocate_ip_address(
        context,
        new_addresses,
        network_id,
        None,
        CONF.QUARK.ipam_reuse_after,
        version=ip_version,
        ip_addresses=[ip_address] if ip_address else [],
        segment_id=segment_id,
        address_type=iptype)
    with context.session.begin():
        address = new_addresses[0]
        new_address = db_api.port_associate_ip(context, ports, address)
    return v._make_ip_dict(new_address)
Ejemplo n.º 50
0
def update_port(context, id, port):
    """Update values of a port.

    : param context: neutron api request context
    : param id: UUID representing the port to update.
    : param port: 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.
    """
    LOG.info("update_port %s for tenant %s" % (id, context.tenant_id))
    port_db = db_api.port_find(context, id=id, scope=db_api.ONE)
    if not port_db:
        raise exceptions.PortNotFound(port_id=id)

    port_dict = port["port"]
    fixed_ips = port_dict.pop("fixed_ips", None)

    admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up",
                  "device_id"]
    always_filter = ["network_id", "backend_key"]
    utils.filter_body(context, port_dict, admin_only=admin_only,
                      always_filter=always_filter)

    # Pre-check the requested fixed_ips before making too many db trips.
    # Note that this is the only check we need, since this call replaces
    # the entirety of the IP addresses document if fixed_ips are provided.
    if fixed_ips:
        quota.QUOTAS.limit_check(context, context.tenant_id,
                                 fixed_ips_per_port=len(fixed_ips))

    # TODO(anyone): security groups are not currently supported on port create,
    #               nor on isolated networks today. Please see RM8615
    new_security_groups = utils.pop_param(port_dict, "security_groups")
    if new_security_groups is not None:
        if not STRATEGY.is_parent_network(port_db["network_id"]):
            raise q_exc.TenantNetworkSecurityGroupsNotImplemented()

    if new_security_groups is not None and not port_db["device_id"]:
        raise q_exc.SecurityGroupsRequireDevice()

    group_ids, security_group_mods = _make_security_group_list(
        context, new_security_groups)
    quota.QUOTAS.limit_check(context, context.tenant_id,
                             security_groups_per_port=len(group_ids))

    if fixed_ips is not None:
        # NOTE(mdietz): we want full control over IPAM since
        #              we're allocating by subnet instead of
        #              network.
        ipam_driver = ipam.IPAM_REGISTRY.get_strategy(
            ipam.QuarkIpamANY.get_name())

        addresses, subnet_ids = [], []
        ip_addresses = {}

        for fixed_ip in fixed_ips:
            subnet_id = fixed_ip.get("subnet_id")
            ip_address = fixed_ip.get("ip_address")
            if not (subnet_id or ip_address):
                raise exceptions.BadRequest(
                    resource="fixed_ips",
                    msg="subnet_id or ip_address required")

            if ip_address and not subnet_id:
                raise exceptions.BadRequest(
                    resource="fixed_ips",
                    msg="subnet_id required for ip_address allocation")

            if subnet_id and ip_address:
                ip_netaddr = netaddr.IPAddress(ip_address).ipv6()
                ip_addresses[ip_netaddr] = subnet_id
            else:
                subnet_ids.append(subnet_id)

        port_ips = set([netaddr.IPAddress(int(a["address"]))
                        for a in port_db["ip_addresses"]])
        new_ips = set([a for a in ip_addresses.keys()])

        ips_to_allocate = list(new_ips - port_ips)
        ips_to_deallocate = list(port_ips - new_ips)

        for ip in ips_to_allocate:
            if ip in ip_addresses:
                # NOTE: Fix for RM10187 - we were losing the list of IPs if
                #       more than one IP was to be allocated. Track an
                #       aggregate list instead, and add it to the running total
                #       after each allocate
                allocated = []
                ipam_driver.allocate_ip_address(
                    context, allocated, port_db["network_id"],
                    port_db["id"], reuse_after=None, ip_addresses=[ip],
                    subnets=[ip_addresses[ip]])
                addresses.extend(allocated)

        for ip in ips_to_deallocate:
            ipam_driver.deallocate_ips_by_port(
                context, port_db, ip_address=ip)

        for subnet_id in subnet_ids:
            ipam_driver.allocate_ip_address(
                context, addresses, port_db["network_id"], port_db["id"],
                reuse_after=CONF.QUARK.ipam_reuse_after,
                subnets=[subnet_id])

        # Need to return all existing addresses and the new ones
        if addresses:
            port_dict["addresses"] = port_db["ip_addresses"]
            port_dict["addresses"].extend(addresses)

    net_driver = registry.DRIVER_REGISTRY.get_driver(
        port_db.network["network_plugin"])

    # TODO(anyone): What do we want to have happen here if this fails? Is it
    #               ok to continue to keep the IPs but fail to apply security
    #               groups? Is there a clean way to have a multi-status? Since
    #               we're in a beta-y status, I'm going to let this sit for
    #               a future patch where we have time to solve it well.
    kwargs = {}
    if new_security_groups is not None:
        kwargs["security_groups"] = security_group_mods
    net_driver.update_port(context, port_id=port_db["backend_key"],
                           mac_address=port_db["mac_address"],
                           device_id=port_db["device_id"],
                           **kwargs)

    port_dict["security_groups"] = security_group_mods

    with context.session.begin():
        port = db_api.port_update(context, port_db, **port_dict)

    # NOTE(mdietz): fix for issue 112, we wanted the IPs to be in
    #              allocated_at order, so get a fresh object every time
    if port_db in context.session:
        context.session.expunge(port_db)
    port_db = db_api.port_find(context, id=id, scope=db_api.ONE)

    return v._make_port_dict(port_db)
Ejemplo n.º 51
0
def create_floatingip(context, content):
    """Allocate or reallocate a floating IP.

    :param context: neutron api request context.
    :param content: dictionary describing the floating ip, with keys
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.  All keys will be populated.

    :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('create_floatingip %s for tenant %s and body %s' %
             (id, context.tenant_id, content))
    tenant_id = content.get('tenant_id')
    network_id = content.get('floating_network_id')
    fixed_ip_address = content.get('fixed_ip_address')
    ip_address = content.get('floating_ip_address')
    port_id = content.get('port_id')

    if not tenant_id:
        tenant_id = context.tenant_id

    if not network_id:
        raise exceptions.BadRequest(resource='floating_ip',
                                    msg='floating_network_id is required.')

    network = db_api.network_find(context, id=network_id, scope=db_api.ONE)

    if not network:
        raise exceptions.NetworkNotFound(net_id=network_id)

    fixed_ip = None
    port = None
    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 not port.ip_addresses or len(port.ip_addresses) == 0:
            raise qex.NoAvailableFixedIpsForPort(port_id=port_id)

        if not fixed_ip_address:
            fixed_ip = _get_next_available_fixed_ip(port)
            if not fixed_ip:
                raise qex.NoAvailableFixedIpsForPort(
                    port_id=port_id)
        else:
            fixed_ip = next((ip for ip in port.ip_addresses
                            if (ip['address_readable'] == fixed_ip_address and
                                ip.get('address_type') == ip_types.FIXED)),
                            None)

            if not fixed_ip:
                raise qex.FixedIpDoesNotExistsForPort(
                    fixed_ip=fixed_ip_address, port_id=port_id)

            if any(ip for ip in port.ip_addresses
                   if (ip.get('address_type') == ip_types.FLOATING and
                       ip.fixed_ip['address_readable'] == fixed_ip_address)):
                raise qex.PortAlreadyContainsFloatingIp(
                    port_id=port_id)

    new_addresses = []
    ip_addresses = []
    if ip_address:
        ip_addresses.append(ip_address)

    seg_name = CONF.QUARK.floating_ip_segment_name
    strategy_name = CONF.QUARK.floating_ip_ipam_strategy

    if strategy_name.upper() == 'NETWORK':
        strategy_name = network.get("ipam_strategy")

    ipam_driver = ipam.IPAM_REGISTRY.get_strategy(strategy_name)
    ipam_driver.allocate_ip_address(context, new_addresses, network_id,
                                    port_id, CONF.QUARK.ipam_reuse_after,
                                    seg_name, version=4,
                                    ip_addresses=ip_addresses,
                                    address_type=ip_types.FLOATING)

    flip = new_addresses[0]

    if fixed_ip and port:
        context.session.begin()
        try:
            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()

            flip_driver.register_floating_ip(flip, port, fixed_ip)
            context.session.commit()
        except Exception:
            context.session.rollback()
            raise

    return v._make_floating_ip_dict(flip, port_id)
Ejemplo n.º 52
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)
Ejemplo n.º 53
0
def create_port(context, port):
    """Create a port

    Create a port which is a connection point of a device (e.g., a VM
    NIC) to attach to a L2 Neutron network.
    : param context: neutron api request context
    : param port: dictionary describing the port, with keys
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.  All keys will be populated.
    """
    LOG.info("create_port for tenant %s" % context.tenant_id)
    port_attrs = port["port"]

    admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up",
                  "use_forbidden_mac_range"]
    utils.filter_body(context, port_attrs, admin_only=admin_only)

    port_attrs = port["port"]
    mac_address = utils.pop_param(port_attrs, "mac_address", None)
    use_forbidden_mac_range = utils.pop_param(port_attrs,
                                              "use_forbidden_mac_range", False)
    segment_id = utils.pop_param(port_attrs, "segment_id")
    fixed_ips = utils.pop_param(port_attrs, "fixed_ips")
    if "device_id" not in port_attrs:
        port_attrs['device_id'] = ""
    device_id = port_attrs['device_id']
    net_id = port_attrs["network_id"]

    port_id = uuidutils.generate_uuid()

    net = db_api.network_find(context, None, None, None, False, id=net_id,
                              scope=db_api.ONE)

    if not net:
        raise exceptions.NetworkNotFound(net_id=net_id)
    _raise_if_unauthorized(context.tenant_id, net)

    # NOTE (Perkins): If a device_id is given, try to prevent multiple ports
    # from being created for a device already attached to the network
    if device_id:
        existing_ports = db_api.port_find(context,
                                          network_id=net_id,
                                          device_id=device_id,
                                          scope=db_api.ONE)
        if existing_ports:
            raise exceptions.BadRequest(
                resource="port", msg="This device is already connected to the "
                "requested network via another port")

    # Try to fail early on quotas and save ourselves some db overhead
    if fixed_ips:
        quota.QUOTAS.limit_check(context, context.tenant_id,
                                 fixed_ips_per_port=len(fixed_ips))

    if not STRATEGY.is_parent_network(net_id):
        # We don't honor segmented networks when they aren't "shared"
        segment_id = None
        port_count = db_api.port_count_all(context, network_id=[net_id],
                                           tenant_id=[context.tenant_id])
        quota.QUOTAS.limit_check(
            context, context.tenant_id,
            ports_per_network=port_count + 1)
    else:
        if not segment_id:
            raise q_exc.AmbiguousNetworkId(net_id=net_id)

    ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])

    net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"])

    # TODO(anyone): security groups are not currently supported on port create,
    #               nor on isolated networks today. Please see RM8615
    security_groups = utils.pop_param(port_attrs, "security_groups")
    if security_groups is not None:
        raise q_exc.SecurityGroupsNotImplemented()

    group_ids, security_groups = _make_security_group_list(context,
                                                           security_groups)
    quota.QUOTAS.limit_check(context, context.tenant_id,
                             security_groups_per_port=len(group_ids))
    addresses = []
    backend_port = None

    with utils.CommandManager().execute() as cmd_mgr:
        @cmd_mgr.do
        def _allocate_ips(fixed_ips, net, port_id, segment_id, mac):
            fixed_ip_kwargs = {}
            if fixed_ips:
                if STRATEGY.is_parent_network(net_id) and not context.is_admin:
                    raise exceptions.NotAuthorized()

                ips, subnets = split_and_validate_requested_subnets(context,
                                                                    net_id,
                                                                    segment_id,
                                                                    fixed_ips)
                fixed_ip_kwargs["ip_addresses"] = ips
                fixed_ip_kwargs["subnets"] = subnets

            ipam_driver.allocate_ip_address(
                context, addresses, net["id"], port_id,
                CONF.QUARK.ipam_reuse_after, segment_id=segment_id,
                mac_address=mac, **fixed_ip_kwargs)

        @cmd_mgr.undo
        def _allocate_ips_undo(addr):
            LOG.info("Rolling back IP addresses...")
            if addresses:
                for address in addresses:
                    try:
                        with context.session.begin():
                            ipam_driver.deallocate_ip_address(context, address)
                    except Exception:
                        LOG.exception("Couldn't release IP %s" % address)

        @cmd_mgr.do
        def _allocate_mac(net, port_id, mac_address,
                          use_forbidden_mac_range=False):
            mac = ipam_driver.allocate_mac_address(
                context, net["id"], port_id, CONF.QUARK.ipam_reuse_after,
                mac_address=mac_address,
                use_forbidden_mac_range=use_forbidden_mac_range)
            return mac

        @cmd_mgr.undo
        def _allocate_mac_undo(mac):
            LOG.info("Rolling back MAC address...")
            if mac:
                try:
                    with context.session.begin():
                        ipam_driver.deallocate_mac_address(context,
                                                           mac["address"])
                except Exception:
                    LOG.exception("Couldn't release MAC %s" % mac)

        @cmd_mgr.do
        def _allocate_backend_port(mac, addresses, net, port_id):
            backend_port = net_driver.create_port(context, net["id"],
                                                  port_id=port_id,
                                                  security_groups=group_ids,
                                                  device_id=device_id)
            return backend_port

        @cmd_mgr.undo
        def _allocate_back_port_undo(backend_port):
            LOG.info("Rolling back backend port...")
            try:
                net_driver.delete_port(context, backend_port["uuid"])
            except Exception:
                LOG.exception(
                    "Couldn't rollback backend port %s" % backend_port)

        @cmd_mgr.do
        def _allocate_db_port(port_attrs, backend_port, addresses, mac):
            port_attrs["network_id"] = net["id"]
            port_attrs["id"] = port_id
            port_attrs["security_groups"] = security_groups

            LOG.info("Including extra plugin attrs: %s" % backend_port)
            port_attrs.update(backend_port)
            with context.session.begin():
                new_port = db_api.port_create(
                    context, addresses=addresses, mac_address=mac["address"],
                    backend_key=backend_port["uuid"], **port_attrs)

            return new_port

        @cmd_mgr.undo
        def _allocate_db_port_undo(new_port):
            LOG.info("Rolling back database port...")
            if not new_port:
                return
            try:
                with context.session.begin():
                    db_api.port_delete(context, new_port)
            except Exception:
                LOG.exception(
                    "Couldn't rollback db port %s" % backend_port)

        # addresses, mac, backend_port, new_port
        mac = _allocate_mac(net, port_id, mac_address,
                            use_forbidden_mac_range=use_forbidden_mac_range)
        _allocate_ips(fixed_ips, net, port_id, segment_id, mac)
        backend_port = _allocate_backend_port(mac, addresses, net, port_id)
        new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac)

    return v._make_port_dict(new_port)