Example #1
0
 def _check_for_dup_router_subnet(self, context, router_id, network_id,
                                  subnet_id):
     try:
         rport_qry = context.session.query(models_v2.Port)
         rports = rport_qry.filter_by(
             device_id=router_id,
             device_owner=DEVICE_OWNER_ROUTER_INTF,
         ).all()
         # its possible these ports on on the same network, but
         # different subnet
         new_cidr = self._get_subnet(context, subnet_id)['cidr']
         new_ipnet = netaddr.IPNetwork(new_cidr)
         for p in rports:
             for ip in p['fixed_ips']:
                 if ip['subnet_id'] == subnet_id:
                     msg = ("Router already has a port on subnet %s" %
                            subnet_id)
                     raise q_exc.BadRequest(resource='router', msg=msg)
                 cidr = self._get_subnet(context, ip['subnet_id'])['cidr']
                 ipnet = netaddr.IPNetwork(cidr)
                 match1 = netaddr.all_matching_cidrs(new_ipnet, [cidr])
                 match2 = netaddr.all_matching_cidrs(ipnet, [new_cidr])
                 if match1 or match2:
                     msg = (("Cidr %s of subnet %s is overlapped " +
                             "with cidr %s of subnet %s") %
                            (new_cidr, subnet_id, cidr, ip['subnet_id']))
                     raise q_exc.BadRequest(resource='router', msg=msg)
     except exc.NoResultFound:
         pass
Example #2
0
    def get_assoc_data(self, context, fip, floating_network_id):
        """When a floating IP is associated with an internal port,
        we need to extract/determine some data associated with the
        internal port, including the internal_ip_address, and router_id.
        We also need to confirm that this internal port is owned by the
        tenant who owns the floating IP.
        """
        internal_port = self._get_port(context, fip['port_id'])
        if not internal_port['tenant_id'] == fip['tenant_id']:
            port_id = fip['port_id']
            if 'id' in fip:
                floatingip_id = fip['id']
                msg = _('Port %(port_id)s is associated with a different '
                        'tenant than Floating IP %(floatingip_id)s and '
                        'therefore cannot be bound.')
            else:
                msg = _('Cannnot create floating IP and bind it to '
                        'Port %(port_id)s, since that port is owned by a '
                        'different tenant.')
            raise q_exc.BadRequest(resource='floatingip', msg=msg % locals())

        internal_subnet_id = None
        if 'fixed_ip_address' in fip and fip['fixed_ip_address']:
            internal_ip_address = fip['fixed_ip_address']
            for ip in internal_port['fixed_ips']:
                if ip['ip_address'] == internal_ip_address:
                    internal_subnet_id = ip['subnet_id']
            if not internal_subnet_id:
                msg = ('Port %s does not have fixed ip %s' %
                       (internal_port['id'], internal_ip_address))
                raise q_exc.BadRequest(resource='floatingip', msg=msg)
        else:
            ips = [ip['ip_address'] for ip in internal_port['fixed_ips']]
            if len(ips) == 0:
                msg = ('Cannot add floating IP to port %s that has'
                       'no fixed IP addresses' % internal_port['id'])
                raise q_exc.BadRequest(resource='floatingip', msg=msg)
            if len(ips) > 1:
                msg = ('Port %s has multiple fixed IPs.  Must provide'
                       ' a specific IP when assigning a floating IP' %
                       internal_port['id'])
                raise q_exc.BadRequest(resource='floatingip', msg=msg)
            internal_ip_address = internal_port['fixed_ips'][0]['ip_address']
            internal_subnet_id = internal_port['fixed_ips'][0]['subnet_id']

        router_id = self._get_router_for_internal_subnet(
            context, internal_port, internal_subnet_id)
        # confirm that this router has a floating
        # ip enabled gateway with support for this floating IP network
        try:
            port_qry = context.elevated().session.query(models_v2.Port)
            ports = port_qry.filter_by(
                network_id=floating_network_id,
                device_id=router_id,
                device_owner=DEVICE_OWNER_ROUTER_GW).one()
        except exc.NoResultFound:
            raise l3.ExternalGatewayForFloatingIPNotFound(
                subnet_id=internal_subnet_id, port_id=internal_port['id'])

        return (fip['port_id'], internal_ip_address, router_id)
Example #3
0
 def _check_for_dup_router_subnet(self, context, router_id, network_id,
                                  subnet_id, subnet_cidr):
     try:
         rport_qry = context.session.query(models_v2.Port)
         rports = rport_qry.filter_by(device_id=router_id)
         # its possible these ports on on the same network, but
         # different subnet
         new_ipnet = netaddr.IPNetwork(subnet_cidr)
         for p in rports:
             for ip in p['fixed_ips']:
                 if ip['subnet_id'] == subnet_id:
                     msg = (_("Router already has a port on subnet %s") %
                            subnet_id)
                     raise q_exc.BadRequest(resource='router', msg=msg)
                 sub_id = ip['subnet_id']
                 cidr = self._get_subnet(context.elevated(), sub_id)['cidr']
                 ipnet = netaddr.IPNetwork(cidr)
                 match1 = netaddr.all_matching_cidrs(new_ipnet, [cidr])
                 match2 = netaddr.all_matching_cidrs(ipnet, [subnet_cidr])
                 if match1 or match2:
                     data = {
                         'subnet_cidr': subnet_cidr,
                         'subnet_id': subnet_id,
                         'cidr': cidr,
                         'sub_id': sub_id
                     }
                     msg = (_("Cidr %(subnet_cidr)s of subnet "
                              "%(subnet_id)s overlaps with cidr %(cidr)s "
                              "of subnet %(sub_id)s") % data)
                     raise q_exc.BadRequest(resource='router', msg=msg)
     except exc.NoResultFound:
         pass
Example #4
0
    def create_floatingip(self, context, floatingip):
        fip = floatingip['floatingip']
        tenant_id = self._get_tenant_id_for_create(context, fip)
        fip_id = uuidutils.generate_uuid()

        f_net_id = fip['floating_network_id']
        if not self._network_is_external(context, f_net_id):
            msg = "Network %s is not a valid external network" % f_net_id
            raise q_exc.BadRequest(resource='floatingip', msg=msg)

        try:
            with context.session.begin(subtransactions=True):
                # This external port is never exposed to the tenant.
                # it is used purely for internal system and admin use when
                # managing floating IPs.
                external_port = self.create_port(
                    context.elevated(),
                    {
                        'port': {
                            'tenant_id': '',  # tenant intentionally not set
                            'network_id': f_net_id,
                            'mac_address': attributes.ATTR_NOT_SPECIFIED,
                            'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
                            'admin_state_up': True,
                            'device_id': fip_id,
                            'device_owner': DEVICE_OWNER_FLOATINGIP,
                            'name': ''
                        }
                    })
                # Ensure IP addresses are allocated on external port
                if not external_port['fixed_ips']:
                    msg = "Unable to find any IP address on external network"
                    raise q_exc.BadRequest(resource='floatingip', msg=msg)

                floating_fixed_ip = external_port['fixed_ips'][0]
                floating_ip_address = floating_fixed_ip['ip_address']
                floatingip_db = FloatingIP(
                    id=fip_id,
                    tenant_id=tenant_id,
                    floating_network_id=fip['floating_network_id'],
                    floating_ip_address=floating_ip_address,
                    floating_port_id=external_port['id'])
                fip['tenant_id'] = tenant_id
                # Update association with internal port
                # and define external IP address
                self._update_fip_assoc(context, fip, floatingip_db,
                                       external_port)
                context.session.add(floatingip_db)
        # TODO(salvatore-orlando): Avoid broad catch
        # Maybe by introducing base class for L3 exceptions
        except q_exc.BadRequest:
            LOG.exception(
                _("Unable to create Floating ip due to a "
                  "malformed request"))
            raise
        except Exception:
            LOG.exception(_("Floating IP association failed"))
            raise

        return self._make_floatingip_dict(floatingip_db)
Example #5
0
    def add_router_interface(self, context, router_id, interface_info):
        # make sure router exists - will raise if not
        self._get_router(context, router_id)
        if not interface_info:
            msg = "Either subnet_id or port_id must be specified"
            raise q_exc.BadRequest(resource='router', msg=msg)

        if 'port_id' in interface_info:
            if 'subnet_id' in interface_info:
                msg = "cannot specify both subnet-id and port-id"
                raise q_exc.BadRequest(resource='router', msg=msg)

            port = self._get_port(context, interface_info['port_id'])
            if port['device_id']:
                raise q_exc.PortInUse(net_id=port['network_id'],
                                      port_id=port['id'],
                                      device_id=port['device_id'])
            fixed_ips = [ip for ip in port['fixed_ips']]
            if len(fixed_ips) != 1:
                msg = 'Router port must have exactly one fixed IP'
                raise q_exc.BadRequest(resource='router', msg=msg)
            self._check_for_dup_router_subnet(context, router_id,
                                              port['network_id'],
                                              fixed_ips[0]['subnet_id'])
            with context.session.begin(subtransactions=True):
                port.update({
                    'device_id': router_id,
                    'device_owner': DEVICE_OWNER_ROUTER_INTF
                })
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            subnet = self._get_subnet(context, subnet_id)
            # Ensure the subnet has a gateway
            if not subnet['gateway_ip']:
                msg = 'Subnet for router interface must have a gateway IP'
                raise q_exc.BadRequest(resource='router', msg=msg)
            self._check_for_dup_router_subnet(context, router_id,
                                              subnet['network_id'], subnet_id)
            fixed_ip = {
                'ip_address': subnet['gateway_ip'],
                'subnet_id': subnet['id']
            }
            port = self.create_port(
                context, {
                    'port': {
                        'network_id': subnet['network_id'],
                        'fixed_ips': [fixed_ip],
                        'mac_address': attributes.ATTR_NOT_SPECIFIED,
                        'admin_state_up': True,
                        'device_id': router_id,
                        'device_owner': DEVICE_OWNER_ROUTER_INTF,
                        'name': ''
                    }
                })
        return {
            'port_id': port['id'],
            'subnet_id': port['fixed_ips'][0]['subnet_id']
        }
Example #6
0
    def _update_router_gw_info(self, context, router_id, info):
        # TODO(salvatore-orlando): guarantee atomic behavior also across
        # operations that span beyond the model classes handled by this
        # class (e.g.: delete_port)
        router = self._get_router(context, router_id)
        gw_port = router.gw_port

        network_id = info.get('network_id', None) if info else None
        if network_id:
            self._get_network(context, network_id)
            if not self._network_is_external(context, network_id):
                msg = "Network %s is not a valid external network" % network_id
                raise q_exc.BadRequest(resource='router', msg=msg)

        # figure out if we need to delete existing port
        if gw_port and gw_port['network_id'] != network_id:
            with context.session.begin(subtransactions=True):
                router.gw_port = None
                context.session.add(router)
            self.delete_port(context.elevated(), gw_port['id'],
                             l3_port_check=False)

        if network_id is not None and (gw_port is None or
                                       gw_port['network_id'] != network_id):
            subnets = self._get_subnets_by_network(context,
                                                   network_id)
            for subnet in subnets:
                self._check_for_dup_router_subnet(context, router_id,
                                                  network_id, subnet['id'])

            # Port has no 'tenant-id', as it is hidden from user
            gw_port = self.create_port(context.elevated(), {
                'port':
                {'tenant_id': '',  # intentionally not set
                 'network_id': network_id,
                 'mac_address': attributes.ATTR_NOT_SPECIFIED,
                 'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
                 'device_id': router_id,
                 'device_owner': DEVICE_OWNER_ROUTER_GW,
                 'admin_state_up': True,
                 'name': ''}})

            if not len(gw_port['fixed_ips']):
                self.delete_port(context.elevated(), gw_port['id'],
                                 l3_port_check=False)
                msg = ('No IPs available for external network %s' %
                       network_id)
                raise q_exc.BadRequest(resource='router', msg=msg)

            with context.session.begin(subtransactions=True):
                router.gw_port = self._get_port(context.elevated(),
                                                gw_port['id'])
                context.session.add(router)
Example #7
0
 def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
     port_id = internal_ip_address = router_id = None
     if (('fixed_ip_address' in fip and fip['fixed_ip_address'])
             and not ('port_id' in fip and fip['port_id'])):
         msg = _("fixed_ip_address cannot be specified without a port_id")
         raise q_exc.BadRequest(resource='floatingip', msg=msg)
     if 'port_id' in fip and fip['port_id']:
         port_id, internal_ip_address, router_id = self.get_assoc_data(
             context, fip, floatingip_db['floating_network_id'])
         fip_qry = context.session.query(FloatingIP)
         try:
             fip_qry.filter_by(
                 fixed_port_id=fip['port_id'],
                 floating_network_id=floatingip_db['floating_network_id'],
                 fixed_ip_address=internal_ip_address).one()
             raise l3.FloatingIPPortAlreadyAssociated(
                 port_id=fip['port_id'],
                 fip_id=floatingip_db['id'],
                 floating_ip_address=floatingip_db['floating_ip_address'],
                 fixed_ip=internal_ip_address,
                 net_id=floatingip_db['floating_network_id'])
         except exc.NoResultFound:
             pass
     floatingip_db.update({
         'fixed_ip_address': internal_ip_address,
         'fixed_port_id': port_id,
         'router_id': router_id
     })
Example #8
0
    def _create_router_gw_port(self, context, router, network_id):
        # Port has no 'tenant-id', as it is hidden from user
        gw_port = self.create_port(
            context.elevated(),
            {
                'port': {
                    'tenant_id': '',  # intentionally not set
                    'network_id': network_id,
                    'mac_address': attributes.ATTR_NOT_SPECIFIED,
                    'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
                    'device_id': router['id'],
                    'device_owner': DEVICE_OWNER_ROUTER_GW,
                    'admin_state_up': True,
                    'name': ''
                }
            })

        if not gw_port['fixed_ips']:
            self.delete_port(context.elevated(),
                             gw_port['id'],
                             l3_port_check=False)
            msg = (_('No IPs available for external network %s') % network_id)
            raise q_exc.BadRequest(resource='router', msg=msg)

        with context.session.begin(subtransactions=True):
            router.gw_port = self._get_port(context.elevated(), gw_port['id'])
            context.session.add(router)
Example #9
0
    def _get_router_for_internal_subnet(self, context, internal_port,
                                        internal_subnet_id):
        subnet_db = self._get_subnet(context, internal_subnet_id)
        if not subnet_db['gateway_ip']:
            msg = ('Cannot add floating IP to port on subnet %s '
                   'which has no gateway_ip' % internal_subnet_id)
            raise q_exc.BadRequest(resource='floatingip', msg=msg)

        #FIXME(danwent): can do join, but cannot use standard F-K syntax?
        # just do it inefficiently for now
        port_qry = context.session.query(models_v2.Port)
        ports = port_qry.filter_by(network_id=internal_port['network_id'])
        for p in ports:
            ips = [ip['ip_address'] for ip in p['fixed_ips']]
            if len(ips) != 1:
                continue
            fixed = p['fixed_ips'][0]
            if (fixed['ip_address'] == subnet_db['gateway_ip']
                    and fixed['subnet_id'] == internal_subnet_id):
                router_qry = context.session.query(Router)
                try:
                    router = router_qry.filter_by(id=p['device_id']).one()
                    return router['id']
                except exc.NoResultFound:
                    pass

        raise l3.ExternalGatewayForFloatingIPNotFound(
            subnet_id=internal_subnet_id, port_id=internal_port['id'])
Example #10
0
    def _get_router_for_floatingip(self, context, internal_port,
                                   internal_subnet_id, external_network_id):
        subnet_db = self._get_subnet(context, internal_subnet_id)
        if not subnet_db['gateway_ip']:
            msg = (_('Cannot add floating IP to port on subnet %s '
                     'which has no gateway_ip') % internal_subnet_id)
            raise q_exc.BadRequest(resource='floatingip', msg=msg)

        # find router interface ports on this network
        router_intf_qry = context.session.query(models_v2.Port)
        router_intf_ports = router_intf_qry.filter_by(
            network_id=internal_port['network_id'],
            device_owner=DEVICE_OWNER_ROUTER_INTF)

        for intf_p in router_intf_ports:
            if intf_p['fixed_ips'][0]['subnet_id'] == internal_subnet_id:
                router_id = intf_p['device_id']
                router_gw_qry = context.session.query(models_v2.Port)
                has_gw_port = router_gw_qry.filter_by(
                    network_id=external_network_id,
                    device_id=router_id,
                    device_owner=DEVICE_OWNER_ROUTER_GW).count()
                if has_gw_port:
                    return router_id

        raise l3.ExternalGatewayForFloatingIPNotFound(
            subnet_id=internal_subnet_id,
            external_network_id=external_network_id,
            port_id=internal_port['id'])
Example #11
0
    def _internal_fip_assoc_data(self, context, fip):
        """Retrieve internal port data for floating IP.

        Retrieve information concerning the internal port where
        the floating IP should be associated to.
        """
        internal_port = self._get_port(context, fip['port_id'])
        if not internal_port['tenant_id'] == fip['tenant_id']:
            port_id = fip['port_id']
            if 'id' in fip:
                floatingip_id = fip['id']
                data = {'port_id': port_id, 'floatingip_id': floatingip_id}
                msg = (_('Port %(port_id)s is associated with a different '
                         'tenant than Floating IP %(floatingip_id)s and '
                         'therefore cannot be bound.') % data)
            else:
                msg = (_('Cannnot create floating IP and bind it to '
                         'Port %s, since that port is owned by a '
                         'different tenant.') % port_id)
            raise q_exc.BadRequest(resource='floatingip', msg=msg)

        internal_subnet_id = None
        if 'fixed_ip_address' in fip and fip['fixed_ip_address']:
            internal_ip_address = fip['fixed_ip_address']
            for ip in internal_port['fixed_ips']:
                if ip['ip_address'] == internal_ip_address:
                    internal_subnet_id = ip['subnet_id']
            if not internal_subnet_id:
                msg = (_('Port %(id)s does not have fixed ip %(address)s') % {
                    'id': internal_port['id'],
                    'address': internal_ip_address
                })
                raise q_exc.BadRequest(resource='floatingip', msg=msg)
        else:
            ips = [ip['ip_address'] for ip in internal_port['fixed_ips']]
            if not ips:
                msg = (_('Cannot add floating IP to port %s that has'
                         'no fixed IP addresses') % internal_port['id'])
                raise q_exc.BadRequest(resource='floatingip', msg=msg)
            if len(ips) > 1:
                msg = (_('Port %s has multiple fixed IPs.  Must provide'
                         ' a specific IP when assigning a floating IP') %
                       internal_port['id'])
                raise q_exc.BadRequest(resource='floatingip', msg=msg)
            internal_ip_address = internal_port['fixed_ips'][0]['ip_address']
            internal_subnet_id = internal_port['fixed_ips'][0]['subnet_id']
        return internal_port, internal_subnet_id, internal_ip_address
Example #12
0
    def update(self, request, id, body=None, **kwargs):
        """Updates the specified entity's attributes"""
        parent_id = kwargs.get(self._parent_id_name)
        try:
            payload = body.copy()
        except AttributeError:
            msg = _("Invalid format: %s") % request.body
            raise exceptions.BadRequest(resource='body', msg=msg)
        payload['id'] = id
        notifier_api.notify(request.context, self._publisher_id,
                            self._resource + '.update.start',
                            notifier_api.CONF.default_notification_level,
                            payload)
        body = Controller.prepare_request_body(request.context,
                                               body,
                                               False,
                                               self._resource,
                                               self._attr_info,
                                               allow_bulk=self._allow_bulk)
        action = self._plugin_handlers[self.UPDATE]
        # Load object to check authz
        # but pass only attributes in the original body and required
        # by the policy engine to the policy 'brain'
        field_list = [
            name for (name, value) in self._attr_info.iteritems()
            if ('required_by_policy' in value and value['required_by_policy']
                or 'default' not in value)
        ]
        orig_obj = self._item(request,
                              id,
                              field_list=field_list,
                              parent_id=parent_id)
        orig_obj.update(body[self._resource])
        try:
            policy.enforce(request.context,
                           action,
                           orig_obj,
                           plugin=self._plugin)
        except exceptions.PolicyNotAuthorized:
            # To avoid giving away information, pretend that it
            # doesn't exist
            raise webob.exc.HTTPNotFound()

        obj_updater = getattr(self._plugin, action)
        kwargs = {self._resource: body}
        if parent_id:
            kwargs[self._parent_id_name] = parent_id
        obj = obj_updater(request.context, id, **kwargs)
        result = {self._resource: self._view(obj)}
        notifier_method = self._resource + '.update.end'
        notifier_api.notify(request.context, self._publisher_id,
                            notifier_method,
                            notifier_api.CONF.default_notification_level,
                            result)
        self._send_dhcp_notification(request.context, result, notifier_method)
        return result
Example #13
0
    def remove_router_interface(self, context, router_id, interface_info):
        # make sure router exists
        router = self._get_router(context, router_id)
        try:
            policy.enforce(context,
                           "extension:router:remove_router_interface",
                           self._make_router_dict(router))
        except q_exc.PolicyNotAuthorized:
            raise l3.RouterNotFound(router_id=router_id)

        if not interface_info:
            msg = "Either subnet_id or port_id must be specified"
            raise q_exc.BadRequest(resource='router', msg=msg)
        if 'port_id' in interface_info:
            port_id = interface_info['port_id']
            port_db = self._get_port(context, port_id)
            if not (port_db['device_owner'] == DEVICE_OWNER_ROUTER_INTF and
                    port_db['device_id'] == router_id):
                raise w_exc.HTTPNotFound("Router %(router_id)s does not have "
                                         " an interface with id %(port_id)s"
                                         % locals())
            if 'subnet_id' in interface_info:
                port_subnet_id = port_db['fixed_ips'][0]['subnet_id']
                if port_subnet_id != interface_info['subnet_id']:
                    raise w_exc.HTTPConflict("subnet_id %s on port does not "
                                             "match requested one (%s)"
                                             % (port_subnet_id,
                                                interface_info['subnet_id']))
            if port_db['device_id'] != router_id:
                raise w_exc.HTTPConflict("port_id %s not used by router" %
                                         port_db['id'])
            self.delete_port(context, port_db['id'], l3_port_check=False)
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            subnet = self._get_subnet(context, subnet_id)
            found = False

            try:
                rport_qry = context.session.query(models_v2.Port)
                ports = rport_qry.filter_by(
                    device_id=router_id,
                    device_owner=DEVICE_OWNER_ROUTER_INTF,
                    network_id=subnet['network_id']).all()

                for p in ports:
                    if p['fixed_ips'][0]['subnet_id'] == subnet_id:
                        self.delete_port(context, p['id'], l3_port_check=False)
                        found = True
                        break
            except exc.NoResultFound:
                pass

            if not found:
                raise w_exc.HTTPNotFound("Router %(router_id)s has no "
                                         "interface on subnet %(subnet_id)s"
                                         % locals())
Example #14
0
def _get_limit_param(request, max_limit):
    """Extract integer limit from request or fail."""
    try:
        limit = int(request.GET.get('limit', 0))
        if limit >= 0:
            return limit
    except ValueError:
        pass
    msg = _("Limit must be an integer 0 or greater and not '%d'")
    raise exceptions.BadRequest(resource='limit', msg=msg)
Example #15
0
    def remove_router_interface(self, context, router_id, interface_info):
        # make sure router exists
        router = self._get_router(context, router_id)
        try:
            policy.enforce(context, "extension:router:remove_router_interface",
                           self._make_router_dict(router))
        except q_exc.PolicyNotAuthorized:
            raise l3.RouterNotFound(router_id=router_id)

        if not interface_info:
            msg = "Either subnet_id or port_id must be specified"
            raise q_exc.BadRequest(resource='router', msg=msg)
        if 'port_id' in interface_info:
            port_id = interface_info['port_id']
            port_db = self._get_port(context, port_id)
            if not (port_db['device_owner'] == DEVICE_OWNER_ROUTER_INTF
                    and port_db['device_id'] == router_id):
                raise l3.RouterInterfaceNotFound(router_id=router_id,
                                                 port_id=port_id)
            if 'subnet_id' in interface_info:
                port_subnet_id = port_db['fixed_ips'][0]['subnet_id']
                if port_subnet_id != interface_info['subnet_id']:
                    raise q_exc.SubnetMismatchForPort(
                        port_id=port_id, subnet_id=interface_info['subnet_id'])
            self._confirm_router_interface_not_in_use(
                context, router_id, port_db['fixed_ips'][0]['subnet_id'])
            self.delete_port(context, port_db['id'], l3_port_check=False)
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            self._confirm_router_interface_not_in_use(context, router_id,
                                                      subnet_id)

            subnet = self._get_subnet(context, subnet_id)
            found = False

            try:
                rport_qry = context.session.query(models_v2.Port)
                ports = rport_qry.filter_by(
                    device_id=router_id,
                    device_owner=DEVICE_OWNER_ROUTER_INTF,
                    network_id=subnet['network_id']).all()

                for p in ports:
                    if p['fixed_ips'][0]['subnet_id'] == subnet_id:
                        self.delete_port(context, p['id'], l3_port_check=False)
                        found = True
                        break
            except exc.NoResultFound:
                pass

            if not found:
                raise l3.RouterInterfaceNotFoundForSubnet(router_id=router_id,
                                                          subnet_id=subnet_id)
        routers = self.get_sync_data(context.elevated(), [router_id])
        l3_rpc_agent_api.L3AgentNofity.routers_updated(context, routers)
Example #16
0
    def create_floatingip(self, context, floatingip):
        fip = floatingip['floatingip']
        tenant_id = self._get_tenant_id_for_create(context, fip)
        fip_id = uuidutils.generate_uuid()

        f_net_id = fip['floating_network_id']
        if not self._network_is_external(context, f_net_id):
            msg = _("Network %s is not a valid external network") % f_net_id
            raise q_exc.BadRequest(resource='floatingip', msg=msg)

        with context.session.begin(subtransactions=True):
            # This external port is never exposed to the tenant.
            # it is used purely for internal system and admin use when
            # managing floating IPs.
            external_port = self.create_port(
                context.elevated(),
                {
                    'port': {
                        'tenant_id': '',  # tenant intentionally not set
                        'network_id': f_net_id,
                        'mac_address': attributes.ATTR_NOT_SPECIFIED,
                        'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
                        'admin_state_up': True,
                        'device_id': fip_id,
                        'device_owner': DEVICE_OWNER_FLOATINGIP,
                        'name': ''
                    }
                })
            # Ensure IP addresses are allocated on external port
            if not external_port['fixed_ips']:
                raise q_exc.ExternalIpAddressExhausted(net_id=f_net_id)

            floating_fixed_ip = external_port['fixed_ips'][0]
            floating_ip_address = floating_fixed_ip['ip_address']
            floatingip_db = FloatingIP(
                id=fip_id,
                tenant_id=tenant_id,
                floating_network_id=fip['floating_network_id'],
                floating_ip_address=floating_ip_address,
                floating_port_id=external_port['id'])
            fip['tenant_id'] = tenant_id
            # Update association with internal port
            # and define external IP address
            self._update_fip_assoc(context, fip, floatingip_db, external_port)
            context.session.add(floatingip_db)

        router_id = floatingip_db['router_id']
        if router_id:
            routers = self.get_sync_data(context.elevated(), [router_id])
            l3_rpc_agent_api.L3AgentNotify.routers_updated(
                context, routers, 'create_floatingip')
        return self._make_floatingip_dict(floatingip_db)
Example #17
0
    def _check_for_dup_router_subnet(self, context, router_id, network_id,
                                     subnet_id):
        try:
            rport_qry = context.session.query(models_v2.Port)
            rports = rport_qry.filter_by(device_id=router_id,
                                         device_owner=DEVICE_OWNER_ROUTER_INTF,
                                         network_id=network_id).all()
            # its possible these ports on on the same network, but
            # different subnet
            for p in rports:
                for ip in p['fixed_ips']:
                    if ip['subnet_id'] == subnet_id:
                        msg = ("Router already has a port on subnet %s" %
                               subnet_id)
                        raise q_exc.BadRequest(resource='router', msg=msg)

        except exc.NoResultFound:
            pass
Example #18
0
    def _update_router_gw_info(self, context, router_id, info, router=None):
        # TODO(salvatore-orlando): guarantee atomic behavior also across
        # operations that span beyond the model classes handled by this
        # class (e.g.: delete_port)
        router = router or self._get_router(context, router_id)
        gw_port = router.gw_port
        # network_id attribute is required by API, so it must be present
        network_id = info['network_id'] if info else None
        if network_id:
            self._get_network(context, network_id)
            if not self._network_is_external(context, network_id):
                msg = _("Network %s is not a valid external "
                        "network") % network_id
                raise q_exc.BadRequest(resource='router', msg=msg)

        # figure out if we need to delete existing port
        if gw_port and gw_port['network_id'] != network_id:
            fip_count = self.get_floatingips_count(context.elevated(),
                                                   {'router_id': [router_id]})
            if fip_count:
                raise l3.RouterExternalGatewayInUseByFloatingIp(
                    router_id=router_id, net_id=gw_port['network_id'])
            if gw_port and gw_port['network_id'] != network_id:
                with context.session.begin(subtransactions=True):
                    router.gw_port = None
                    context.session.add(router)
                self.delete_port(context.elevated(),
                                 gw_port['id'],
                                 l3_port_check=False)

        if network_id is not None and (gw_port is None
                                       or gw_port['network_id'] != network_id):
            subnets = self._get_subnets_by_network(context, network_id)
            for subnet in subnets:
                self._check_for_dup_router_subnet(context, router_id,
                                                  network_id, subnet['id'],
                                                  subnet['cidr'])
            self._create_router_gw_port(context, router, network_id)
Example #19
0
    def remove_router_interface(self, context, router_id, interface_info):
        if not interface_info:
            msg = _("Either subnet_id or port_id must be specified")
            raise q_exc.BadRequest(resource='router', msg=msg)
        if 'port_id' in interface_info:
            port_id = interface_info['port_id']
            port_db = self._get_port(context, port_id)
            if not (port_db['device_owner'] == DEVICE_OWNER_ROUTER_INTF
                    and port_db['device_id'] == router_id):
                raise l3.RouterInterfaceNotFound(router_id=router_id,
                                                 port_id=port_id)
            if 'subnet_id' in interface_info:
                port_subnet_id = port_db['fixed_ips'][0]['subnet_id']
                if port_subnet_id != interface_info['subnet_id']:
                    raise q_exc.SubnetMismatchForPort(
                        port_id=port_id, subnet_id=interface_info['subnet_id'])
            subnet_id = port_db['fixed_ips'][0]['subnet_id']
            subnet = self._get_subnet(context, subnet_id)
            self._confirm_router_interface_not_in_use(context, router_id,
                                                      subnet_id)
            _network_id = port_db['network_id']
            self.delete_port(context, port_db['id'], l3_port_check=False)
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            self._confirm_router_interface_not_in_use(context, router_id,
                                                      subnet_id)

            subnet = self._get_subnet(context, subnet_id)
            found = False

            try:
                rport_qry = context.session.query(models_v2.Port)
                ports = rport_qry.filter_by(
                    device_id=router_id,
                    device_owner=DEVICE_OWNER_ROUTER_INTF,
                    network_id=subnet['network_id'])

                for p in ports:
                    if p['fixed_ips'][0]['subnet_id'] == subnet_id:
                        port_id = p['id']
                        _network_id = p['network_id']
                        self.delete_port(context, p['id'], l3_port_check=False)
                        found = True
                        break
            except exc.NoResultFound:
                pass

            if not found:
                raise l3.RouterInterfaceNotFoundForSubnet(router_id=router_id,
                                                          subnet_id=subnet_id)
        routers = self.get_sync_data(context.elevated(), [router_id])
        l3_rpc_agent_api.L3AgentNotify.routers_updated(
            context, routers, 'remove_router_interface', {
                'network_id': _network_id,
                'subnet_id': subnet_id
            })
        info = {
            'id': router_id,
            'tenant_id': subnet['tenant_id'],
            'port_id': port_id,
            'subnet_id': subnet_id
        }
        notifier_api.notify(context, notifier_api.publisher_id('network'),
                            'router.interface.delete',
                            notifier_api.CONF.default_notification_level,
                            {'router.interface': info})
        return info
Example #20
0
    def add_router_interface(self, context, router_id, interface_info):
        if not interface_info:
            msg = _("Either subnet_id or port_id must be specified")
            raise q_exc.BadRequest(resource='router', msg=msg)

        if 'port_id' in interface_info:
            if 'subnet_id' in interface_info:
                msg = _("Cannot specify both subnet-id and port-id")
                raise q_exc.BadRequest(resource='router', msg=msg)

            port = self._get_port(context, interface_info['port_id'])
            if port['device_id']:
                raise q_exc.PortInUse(net_id=port['network_id'],
                                      port_id=port['id'],
                                      device_id=port['device_id'])
            fixed_ips = [ip for ip in port['fixed_ips']]
            if len(fixed_ips) != 1:
                msg = _('Router port must have exactly one fixed IP')
                raise q_exc.BadRequest(resource='router', msg=msg)
            subnet_id = fixed_ips[0]['subnet_id']
            subnet = self._get_subnet(context, subnet_id)
            self._check_for_dup_router_subnet(context, router_id,
                                              port['network_id'], subnet['id'],
                                              subnet['cidr'])
            port.update({
                'device_id': router_id,
                'device_owner': DEVICE_OWNER_ROUTER_INTF
            })
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            subnet = self._get_subnet(context, subnet_id)
            # Ensure the subnet has a gateway
            if not subnet['gateway_ip']:
                msg = _('Subnet for router interface must have a gateway IP')
                raise q_exc.BadRequest(resource='router', msg=msg)
            self._check_for_dup_router_subnet(context, router_id,
                                              subnet['network_id'], subnet_id,
                                              subnet['cidr'])
            fixed_ip = {
                'ip_address': subnet['gateway_ip'],
                'subnet_id': subnet['id']
            }
            port = self.create_port(
                context, {
                    'port': {
                        'tenant_id': subnet['tenant_id'],
                        'network_id': subnet['network_id'],
                        'fixed_ips': [fixed_ip],
                        'mac_address': attributes.ATTR_NOT_SPECIFIED,
                        'admin_state_up': True,
                        'device_id': router_id,
                        'device_owner': DEVICE_OWNER_ROUTER_INTF,
                        'name': ''
                    }
                })

        routers = self.get_sync_data(context.elevated(), [router_id])
        l3_rpc_agent_api.L3AgentNotify.routers_updated(
            context, routers, 'add_router_interface', {
                'network_id': port['network_id'],
                'subnet_id': subnet_id
            })
        info = {
            'id': router_id,
            'tenant_id': subnet['tenant_id'],
            'port_id': port['id'],
            'subnet_id': port['fixed_ips'][0]['subnet_id']
        }
        notifier_api.notify(context, notifier_api.publisher_id('network'),
                            'router.interface.create',
                            notifier_api.CONF.default_notification_level,
                            {'router.interface': info})
        return info
def paginate_query(query, model, limit, sorts, marker_obj=None):
    """Returns a query with sorting / pagination criteria added.

    Pagination works by requiring a unique sort key, specified by sorts.
    (If sort keys is not unique, then we risk looping through values.)
    We use the last row in the previous page as the 'marker' for pagination.
    So we must return values that follow the passed marker in the order.
    With a single-valued sort key, this would be easy: sort_key > X.
    With a compound-values sort key, (k1, k2, k3) we must do this to repeat
    the lexicographical ordering:
    (k1 > X1) or (k1 == X1 && k2 > X2) or (k1 == X1 && k2 == X2 && k3 > X3)
    The reason of didn't use OFFSET clause was it don't scale, please refer
    discussion at https://lists.launchpad.net/openstack/msg02547.html

    We also have to cope with different sort directions.

    Typically, the id of the last row is used as the client-facing pagination
    marker, then the actual marker object must be fetched from the db and
    passed in to us as marker.

    :param query: the query object to which we should add paging/sorting
    :param model: the ORM model class
    :param limit: maximum number of items to return
    :param sorts: array of attributes and direction by which results should
                 be sorted
    :param marker: the last item of the previous page; we returns the next
                    results after this value.
    :rtype: sqlalchemy.orm.query.Query
    :return: The query with sorting/pagination added.
    """
    if not sorts:
        return query

    # A primary key must be specified in sort keys
    assert not (limit and len(
        set(dict(sorts).keys())
        & set(model.__table__.primary_key.columns.keys())) == 0)

    # Add sorting
    for sort_key, sort_direction in sorts:
        sort_dir_func = sqlalchemy.asc if sort_direction else sqlalchemy.desc
        try:
            sort_key_attr = getattr(model, sort_key)
        except AttributeError:
            # Extension attribute doesn't support for sorting. Because it
            # existed in attr_info, it will be catched at here
            msg = _("%s is invalid attribute for sort_key") % sort_key
            raise q_exc.BadRequest(resource=model.__tablename__, msg=msg)
        if isinstance(sort_key_attr.property, RelationshipProperty):
            msg = _("The attribute '%(attr)s' is reference to other "
                    "resource, can't used by sort "
                    "'%(resource)s'") % {
                        'attr': sort_key,
                        'resource': model.__tablename__
                    }
            raise q_exc.BadRequest(resource=model.__tablename__, msg=msg)
        query = query.order_by(sort_dir_func(sort_key_attr))

    # Add pagination
    if marker_obj:
        marker_values = [getattr(marker_obj, sort[0]) for sort in sorts]

        # Build up an array of sort criteria as in the docstring
        criteria_list = []
        for i, sort in enumerate(sorts):
            crit_attrs = [(getattr(model, sorts[j][0]) == marker_values[j])
                          for j in xrange(i)]
            model_attr = getattr(model, sort[0])
            if sort[1]:
                crit_attrs.append((model_attr > marker_values[i]))
            else:
                crit_attrs.append((model_attr < marker_values[i]))

            criteria = sqlalchemy.sql.and_(*crit_attrs)
            criteria_list.append(criteria)

        f = sqlalchemy.sql.or_(*criteria_list)
        query = query.filter(f)

    if limit:
        query = query.limit(limit)

    return query
Example #22
0
    def add_router_interface(self, context, router_id, interface_info):
        # make sure router exists
        router = self._get_router(context, router_id)
        if not interface_info:
            msg = "Either subnet_id or port_id must be specified"
            raise q_exc.BadRequest(resource='router', msg=msg)

        try:
            policy.enforce(context, "extension:router:add_router_interface",
                           self._make_router_dict(router))
        except q_exc.PolicyNotAuthorized:
            raise l3.RouterNotFound(router_id=router_id)

        if 'port_id' in interface_info:
            if 'subnet_id' in interface_info:
                msg = "cannot specify both subnet-id and port-id"
                raise q_exc.BadRequest(resource='router', msg=msg)

            port = self._get_port(context, interface_info['port_id'])
            if port['device_id']:
                raise q_exc.PortInUse(net_id=port['network_id'],
                                      port_id=port['id'],
                                      device_id=port['device_id'])
            fixed_ips = [ip for ip in port['fixed_ips']]
            if len(fixed_ips) != 1:
                msg = 'Router port must have exactly one fixed IP'
                raise q_exc.BadRequest(resource='router', msg=msg)
            self._check_for_dup_router_subnet(context, router_id,
                                              port['network_id'],
                                              fixed_ips[0]['subnet_id'])
            port.update({
                'device_id': router_id,
                'device_owner': DEVICE_OWNER_ROUTER_INTF
            })
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            subnet = self._get_subnet(context, subnet_id)
            # Ensure the subnet has a gateway
            if not subnet['gateway_ip']:
                msg = 'Subnet for router interface must have a gateway IP'
                raise q_exc.BadRequest(resource='router', msg=msg)
            self._check_for_dup_router_subnet(context, router_id,
                                              subnet['network_id'], subnet_id)
            fixed_ip = {
                'ip_address': subnet['gateway_ip'],
                'subnet_id': subnet['id']
            }
            port = self.create_port(
                context, {
                    'port': {
                        'tenant_id': subnet['tenant_id'],
                        'network_id': subnet['network_id'],
                        'fixed_ips': [fixed_ip],
                        'mac_address': attributes.ATTR_NOT_SPECIFIED,
                        'admin_state_up': True,
                        'device_id': router_id,
                        'device_owner': DEVICE_OWNER_ROUTER_INTF,
                        'name': ''
                    }
                })

        routers = self.get_sync_data(context.elevated(), [router_id])
        l3_rpc_agent_api.L3AgentNofity.routers_updated(context, routers)
        return {
            'port_id': port['id'],
            'subnet_id': port['fixed_ips'][0]['subnet_id']
        }