Esempio n. 1
0
def split_and_validate_requested_subnets(context, net_id, segment_id,
                                         fixed_ips):
    subnets = []
    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:
            raise exceptions.BadRequest(resource="fixed_ips",
                                        msg="subnet_id required")
        if ip_address:
            ip_addresses[ip_address] = subnet_id
        else:
            subnets.append(subnet_id)

    subnets = ip_addresses.values() + subnets

    sub_models = db_api.subnet_find(context, id=subnets, scope=db_api.ALL)
    if len(sub_models) == 0:
        raise exceptions.NotFound(msg="Requested subnet(s) not found")

    for s in sub_models:
        if s["network_id"] != net_id:
            raise exceptions.InvalidInput(
                error_message="Requested subnet doesn't belong to requested "
                              "network")

        if segment_id and segment_id != s["segment_id"]:
            raise q_exc.AmbiguousNetworkId(net_id=net_id)

    return ip_addresses, subnets
Esempio n. 2
0
def network_find(context, fields=None, **filters):
    ids = []
    defaults = []
    if "id" in filters:
        ids, defaults = STRATEGY.split_network_ids(context, filters["id"])
        filters["ids"] = ids

    if "shared" in filters and True in filters["shared"]:
        defaults = STRATEGY.get_assignable_networks(context)
        if ids:
            defaults = [net for net in ids if net in defaults]
            filters.pop("id")
        if not defaults:
            return []

        if "segment_id" in filters and filters["segment_id"]:
            # Ambiguous search, say we can't find anything
            if len(defaults) > 1:
                raise quark_exc.AmbiguousNetworkId()
            defaults = [STRATEGY.best_match_network_id(
                context, filters["id"][0], filters["segment_id"])]

    query = context.session.query(models.Network)
    model_filters = _model_query(context, models.Network, filters, query)

    if defaults:
        query = query.filter(or_(models.Network.id.in_(defaults),
                             *model_filters))
    else:
        query = query.filter(*model_filters)
    return query
Esempio n. 3
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)
Esempio n. 4
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)
Esempio n. 5
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"]
    utils.filter_body(context, port_attrs, admin_only=admin_only)

    port_attrs = port["port"]
    mac_address = utils.pop_param(port_attrs, "mac_address", None)
    segment_id = utils.pop_param(port_attrs, "segment_id")
    fixed_ips = utils.pop_param(port_attrs, "fixed_ips")
    net_id = port_attrs["network_id"]
    device_id = port_attrs["device_id"]

    port_id = uuidutils.generate_uuid()

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

    if not net:
        raise exceptions.NetworkNotFound(net_id=net_id)

    # 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")

    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"])
    group_ids, security_groups = v.make_security_group_list(
        context, port["port"].pop("security_groups", None))

    addresses = []
    mac = None
    backend_port = None

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

        @cmd_mgr.do
        def _allocate_ips(fixed_ips, net, port_id, segment_id, mac):
            if fixed_ips:
                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")
                    ipam_driver.allocate_ip_address(
                        context,
                        addresses,
                        net["id"],
                        port_id,
                        CONF.QUARK.ipam_reuse_after,
                        segment_id=segment_id,
                        ip_address=ip_address,
                        subnets=[subnet_id],
                        mac_address=mac)
            else:
                ipam_driver.allocate_ip_address(context,
                                                addresses,
                                                net["id"],
                                                port_id,
                                                CONF.QUARK.ipam_reuse_after,
                                                segment_id=segment_id,
                                                mac_address=mac)

        @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):
            mac = ipam_driver.allocate_mac_address(context,
                                                   net["id"],
                                                   port_id,
                                                   CONF.QUARK.ipam_reuse_after,
                                                   mac_address=mac_address)
            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)
        _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)