Esempio n. 1
0
class Route(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.SubnetRoute

    primary_keys = ['destination', 'nexthop', 'subnet_id']

    foreign_keys = {'Subnet': {'subnet_id': 'id'}}

    fields = {
        'subnet_id': common_types.UUIDField(),
        'destination': common_types.IPNetworkField(),
        'nexthop': obj_fields.IPAddressField()
    }

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Route, cls).modify_fields_from_db(db_obj)
        if 'destination' in result:
            result['destination'] = utils.AuthenticIPNetwork(
                result['destination'])
        if 'nexthop' in result:
            result['nexthop'] = netaddr.IPAddress(result['nexthop'])
        return result

    @classmethod
    def modify_fields_to_db(cls, fields):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Route, cls).modify_fields_to_db(fields)
        if 'destination' in result:
            result['destination'] = cls.filter_to_str(result['destination'])
        if 'nexthop' in fields:
            result['nexthop'] = cls.filter_to_str(result['nexthop'])
        return result
Esempio n. 2
0
class IPAllocation(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.IPAllocation

    fields = {
        'port_id': obj_fields.UUIDField(nullable=True),
        'subnet_id': obj_fields.UUIDField(),
        'network_id': obj_fields.UUIDField(),
        'ip_address': obj_fields.IPAddressField(),
    }

    primary_keys = ['subnet_id', 'network_id', 'ip_address']

    foreign_keys = {
        'Port': {'port_id': 'id'},
    }

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(IPAllocation, cls).modify_fields_to_db(fields)
        if 'ip_address' in result:
            result['ip_address'] = cls.filter_to_str(result['ip_address'])
        return result

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(IPAllocation, cls).modify_fields_from_db(db_obj)
        if 'ip_address' in fields:
            fields['ip_address'] = netaddr.IPAddress(fields['ip_address'])
        return fields
Esempio n. 3
0
class Subnet(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.Subnet

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(nullable=True),
        'name':
        obj_fields.StringField(nullable=True),
        'network_id':
        common_types.UUIDField(),
        'segment_id':
        common_types.UUIDField(nullable=True),
        # NOTE: subnetpool_id can be 'prefix_delegation' string
        # when the IPv6 Prefix Delegation is enabled
        'subnetpool_id':
        obj_fields.StringField(nullable=True),
        'ip_version':
        common_types.IPVersionEnumField(),
        'cidr':
        common_types.IPNetworkField(),
        'gateway_ip':
        obj_fields.IPAddressField(nullable=True),
        'allocation_pools':
        obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True),
        'enable_dhcp':
        obj_fields.BooleanField(nullable=True),
        'shared':
        obj_fields.BooleanField(nullable=True),
        'dns_nameservers':
        obj_fields.ListOfObjectsField('DNSNameServer', nullable=True),
        'host_routes':
        obj_fields.ListOfObjectsField('Route', nullable=True),
        'ipv6_ra_mode':
        common_types.IPV6ModeEnumField(nullable=True),
        'ipv6_address_mode':
        common_types.IPV6ModeEnumField(nullable=True),
        'service_types':
        obj_fields.ListOfStringsField(nullable=True)
    }

    synthetic_fields = [
        'allocation_pools', 'dns_nameservers', 'host_routes', 'service_types',
        'shared'
    ]

    foreign_keys = {'Network': {'network_id': 'id'}}

    fields_no_update = ['project_id', 'network_id']

    fields_need_translation = {'host_routes': 'routes'}

    def __init__(self, context=None, **kwargs):
        super(Subnet, self).__init__(context, **kwargs)
        self.add_extra_filter_name('shared')

    def obj_load_attr(self, attrname):
        if attrname == 'shared':
            return self._load_shared()
        if attrname == 'service_types':
            return self._load_service_types()
        super(Subnet, self).obj_load_attr(attrname)

    def _load_shared(self, db_obj=None):
        if db_obj:
            # NOTE(korzen) db_obj is passed when Subnet object is loaded
            # from DB
            rbac_entries = db_obj.get('rbac_entries') or {}
            shared = (rbac_db.RbacNeutronDbObjectMixin.is_network_shared(
                self.obj_context, rbac_entries))
        else:
            # NOTE(korzen) this case is used when Subnet object was
            # instantiated and without DB interaction (get_object(s), update,
            # create), it should be rare case to load 'shared' by that method
            shared = (rbac_db.RbacNeutronDbObjectMixin.get_shared_with_tenant(
                self.obj_context.elevated(), network.NetworkRBAC,
                self.network_id, self.project_id))
        setattr(self, 'shared', shared)
        self.obj_reset_changes(['shared'])

    def _load_service_types(self, db_obj=None):
        if db_obj:
            service_types = db_obj.get('service_types', [])
        else:
            service_types = SubnetServiceType.get_objects(self.obj_context,
                                                          subnet_id=self.id)

        self.service_types = [
            service_type['service_type'] for service_type in service_types
        ]
        self.obj_reset_changes(['service_types'])

    def from_db_object(self, db_obj):
        super(Subnet, self).from_db_object(db_obj)
        self._load_shared(db_obj)
        self._load_service_types(db_obj)

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Subnet, cls).modify_fields_from_db(db_obj)
        if 'cidr' in result:
            result['cidr'] = utils.AuthenticIPNetwork(result['cidr'])
        if 'gateway_ip' in result and result['gateway_ip'] is not None:
            result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip'])
        return result

    @classmethod
    def modify_fields_to_db(cls, fields):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Subnet, cls).modify_fields_to_db(fields)
        if 'cidr' in result:
            result['cidr'] = cls.filter_to_str(result['cidr'])
        if 'gateway_ip' in result and result['gateway_ip'] is not None:
            result['gateway_ip'] = cls.filter_to_str(result['gateway_ip'])
        return result

    @classmethod
    def find_candidate_subnets(cls, context, network_id, host, service_type,
                               fixed_configured):
        """Find canditate subnets for the network, host, and service_type"""
        query = cls.query_subnets_on_network(context, network_id)
        query = SubnetServiceType.query_filter_service_subnets(
            query, service_type)

        # Select candidate subnets and return them
        if not cls.is_host_set(host):
            if fixed_configured:
                # If fixed_ips in request and host is not known all subnets on
                # the network are candidates. Host/Segment will be validated
                # on port update with binding:host_id set. Allocation _cannot_
                # be deferred as requested fixed_ips would then be lost.
                return query.all()
            # If the host isn't known, we can't allocate on a routed network.
            # So, exclude any subnets attached to segments.
            return cls._query_exclude_subnets_on_segments(query).all()

        # The host is known. Consider both routed and non-routed networks
        results = cls._query_filter_by_segment_host_mapping(query, host).all()

        # For now, we're using a simplifying assumption that a host will only
        # touch one segment in a given routed network.  Raise exception
        # otherwise.  This restriction may be relaxed as use cases for multiple
        # mappings are understood.
        segment_ids = {
            subnet.segment_id
            for subnet, mapping in results if mapping
        }
        if 1 < len(segment_ids):
            raise segment_exc.HostConnectedToMultipleSegments(
                host=host, network_id=network_id)

        return [subnet for subnet, _mapping in results]

    @classmethod
    def _query_filter_by_segment_host_mapping(cls, query, host):
        # TODO(tuanvu): find OVO-like solution for handling "join queries" and
        #               write unit test for this function
        """Excludes subnets on segments not reachable by the host

        The query gets two kinds of subnets: those that are on segments that
        the host can reach and those that are not on segments at all (assumed
        reachable by all hosts). Hence, subnets on segments that the host
        *cannot* reach are excluded.
        """
        SegmentHostMapping = segment_model.SegmentHostMapping

        # A host has been provided.  Consider these two scenarios
        # 1. Not a routed network:  subnets are not on segments
        # 2. Is a routed network:  only subnets on segments mapped to host
        # The following join query returns results for either.  The two are
        # guaranteed to be mutually exclusive when subnets are created.
        query = query.add_entity(SegmentHostMapping)
        query = query.outerjoin(
            SegmentHostMapping,
            and_(cls.db_model.segment_id == SegmentHostMapping.segment_id,
                 SegmentHostMapping.host == host))

        # Essentially "segment_id IS NULL XNOR host IS NULL"
        query = query.filter(
            or_(
                and_(cls.db_model.segment_id.isnot(None),
                     SegmentHostMapping.host.isnot(None)),
                and_(cls.db_model.segment_id.is_(None),
                     SegmentHostMapping.host.is_(None))))
        return query

    @classmethod
    def query_subnets_on_network(cls, context, network_id):
        query = model_query.get_collection_query(context, cls.db_model)
        return query.filter(cls.db_model.network_id == network_id)

    @classmethod
    def _query_exclude_subnets_on_segments(cls, query):
        """Excludes all subnets associated with segments

        For the case where the host is not known, we don't consider any subnets
        that are on segments. But, we still consider subnets that are not
        associated with any segment (i.e. for non-routed networks).
        """
        return query.filter(cls.db_model.segment_id.is_(None))

    @classmethod
    def is_host_set(cls, host):
        """Utility to tell if the host is set in the port binding"""
        # This seems redundant, but its not. Host is unset if its None, '',
        # or ATTR_NOT_SPECIFIED due to differences in host binding
        # implementations.
        return host and validators.is_attr_set(host)

    @classmethod
    def network_has_no_subnet(cls, context, network_id, host, service_type):
        # Determine why we found no subnets to raise the right error
        query = cls.query_subnets_on_network(context, network_id)

        if cls.is_host_set(host):
            # Empty because host isn't mapped to a segment with a subnet?
            s_query = query.filter(cls.db_model.segment_id.isnot(None))
            if s_query.limit(1).count() != 0:
                # It is a routed network but no subnets found for host
                raise segment_exc.HostNotConnectedToAnySegment(
                    host=host, network_id=network_id)

        if not query.limit(1).count():
            # Network has *no* subnets of any kind. This isn't an error.
            return True

        # Does filtering ineligible service subnets makes the list empty?
        query = SubnetServiceType.query_filter_service_subnets(
            query, service_type)
        if query.limit(1).count():
            # No, must be a deferred IP port because there are matching
            # subnets. Happens on routed networks when host isn't known.
            raise ipam_exceptions.DeferIpam()
        return False
Esempio n. 4
0
class IPAllocation(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.IPAllocation

    fields = {
        'port_id': common_types.UUIDField(nullable=True),
        'subnet_id': common_types.UUIDField(),
        'network_id': common_types.UUIDField(),
        'ip_address': obj_fields.IPAddressField(),
    }

    fields_no_update = fields.keys()

    primary_keys = ['subnet_id', 'network_id', 'ip_address']

    foreign_keys = {
        'Port': {
            'port_id': 'id'
        },
    }

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(IPAllocation, cls).modify_fields_to_db(fields)
        if 'ip_address' in result:
            result['ip_address'] = cls.filter_to_str(result['ip_address'])
        return result

    # TODO(rossella_s): get rid of it once we switch the db model to using
    # custom types.
    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(IPAllocation, cls).modify_fields_from_db(db_obj)
        if 'ip_address' in fields:
            fields['ip_address'] = netaddr.IPAddress(fields['ip_address'])
        return fields

    @classmethod
    def get_alloc_by_subnet_id(cls,
                               context,
                               subnet_id,
                               device_owner,
                               exclude=True):
        # need to join with ports table as IPAllocation's port
        # is not joined eagerly and thus producing query which yields
        # incorrect results
        if exclude:
            alloc_db = (
                context.session.query(models_v2.IPAllocation).filter_by(
                    subnet_id=subnet_id).join(models_v2.Port).
                filter(~models_v2.Port.device_owner.in_(device_owner)).first())
        else:
            alloc_db = (context.session.query(
                models_v2.IPAllocation).filter_by(subnet_id=subnet_id).join(
                    models_v2.Port).filter(
                        models_v2.Port.device_owner.in_(device_owner)).first())
        if exclude and alloc_db:
            return super(IPAllocation, cls)._load_object(context, alloc_db)
        if alloc_db:
            return True
Esempio n. 5
0
class FloatingIP(base.NeutronDbObject):
    # Version 1.0: Initial version
    # Version 1.1: Added qos_policy_id field
    VERSION = '1.1'

    db_model = l3.FloatingIP

    fields = {
        'id': common_types.UUIDField(),
        'project_id': obj_fields.StringField(nullable=True),
        'floating_ip_address': obj_fields.IPAddressField(),
        'floating_network_id': common_types.UUIDField(),
        'floating_port_id': common_types.UUIDField(),
        'fixed_port_id': common_types.UUIDField(nullable=True),
        'fixed_ip_address': obj_fields.IPAddressField(nullable=True),
        'qos_policy_id': common_types.UUIDField(nullable=True, default=None),
        'router_id': common_types.UUIDField(nullable=True),
        'last_known_router_id': common_types.UUIDField(nullable=True),
        'status': common_types.FloatingIPStatusEnumField(nullable=True),
        'dns': obj_fields.ObjectField('FloatingIPDNS', nullable=True),
    }
    fields_no_update = [
        'project_id', 'floating_ip_address', 'floating_network_id',
        'floating_port_id'
    ]
    synthetic_fields = [
        'dns',
        'qos_policy_id',
    ]

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        result = super(FloatingIP, cls).modify_fields_from_db(db_obj)
        if 'fixed_ip_address' in result:
            result['fixed_ip_address'] = netaddr.IPAddress(
                result['fixed_ip_address'])
        if 'floating_ip_address' in result:
            result['floating_ip_address'] = netaddr.IPAddress(
                result['floating_ip_address'])
        return result

    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(FloatingIP, cls).modify_fields_to_db(fields)
        if 'fixed_ip_address' in result:
            if result['fixed_ip_address'] is not None:
                result['fixed_ip_address'] = cls.filter_to_str(
                    result['fixed_ip_address'])
        if 'floating_ip_address' in result:
            result['floating_ip_address'] = cls.filter_to_str(
                result['floating_ip_address'])
        return result

    def _attach_qos_policy(self, qos_policy_id):
        qos_binding.QosPolicyFloatingIPBinding.delete_objects(self.obj_context,
                                                              fip_id=self.id)
        if qos_policy_id:
            fip_binding_obj = qos_binding.QosPolicyFloatingIPBinding(
                self.obj_context, policy_id=qos_policy_id, fip_id=self.id)
            fip_binding_obj.create()

        self.qos_policy_id = qos_policy_id
        self.obj_reset_changes(['qos_policy_id'])

    def create(self):
        fields = self.obj_get_changes()
        with self.db_context_writer(self.obj_context):
            qos_policy_id = self.qos_policy_id
            super(FloatingIP, self).create()
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(qos_policy_id)

    def update(self):
        fields = self.obj_get_changes()
        with self.db_context_writer(self.obj_context):
            super(FloatingIP, self).update()
            if 'qos_policy_id' in fields:
                self._attach_qos_policy(fields['qos_policy_id'])

    def from_db_object(self, db_obj):
        super(FloatingIP, self).from_db_object(db_obj)
        fields_to_change = []
        if db_obj.get('qos_policy_binding'):
            self.qos_policy_id = db_obj.qos_policy_binding.policy_id
            fields_to_change.append('qos_policy_id')

        self.obj_reset_changes(fields_to_change)

    def obj_make_compatible(self, primitive, target_version):
        _target_version = versionutils.convert_version_to_tuple(target_version)
        if _target_version < (1, 1):
            primitive.pop('qos_policy_id', None)

    @classmethod
    def get_scoped_floating_ips(cls, context, router_ids):
        query = context.session.query(l3.FloatingIP,
                                      models_v2.SubnetPool.address_scope_id)
        query = query.join(models_v2.Port,
                           l3.FloatingIP.fixed_port_id == models_v2.Port.id)
        # Outer join of Subnet can cause each ip to have more than one row.
        query = query.outerjoin(
            models_v2.Subnet,
            models_v2.Subnet.network_id == models_v2.Port.network_id)
        query = query.filter(models_v2.Subnet.ip_version == 4)
        query = query.outerjoin(
            models_v2.SubnetPool,
            models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)

        # Filter out on router_ids
        query = query.filter(l3.FloatingIP.router_id.in_(router_ids))
        return cls._unique_floatingip_iterator(context, query)

    @classmethod
    def _unique_floatingip_iterator(cls, context, query):
        """Iterates over only one row per floating ip. Ignores others."""
        # Group rows by fip id. They must be sorted by same.
        q = query.order_by(l3.FloatingIP.id)
        keyfunc = lambda row: row[0]['id']
        group_iterator = itertools.groupby(q, keyfunc)

        # Just hit the first row of each group
        for key, value in group_iterator:
            # pylint: disable=stop-iteration-return
            row = list(next(value))
            yield (cls._load_object(context, row[0]), row[1])
Esempio n. 6
0
class Subnet(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.Subnet

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(nullable=True),
        'name':
        obj_fields.StringField(nullable=True),
        'network_id':
        common_types.UUIDField(),
        'segment_id':
        common_types.UUIDField(nullable=True),
        # NOTE: subnetpool_id can be 'prefix_delegation' string
        # when the IPv6 Prefix Delegation is enabled
        'subnetpool_id':
        obj_fields.StringField(nullable=True),
        'ip_version':
        common_types.IPVersionEnumField(),
        'cidr':
        common_types.IPNetworkField(),
        'gateway_ip':
        obj_fields.IPAddressField(nullable=True),
        'allocation_pools':
        obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True),
        'enable_dhcp':
        obj_fields.BooleanField(nullable=True),
        'shared':
        obj_fields.BooleanField(nullable=True),
        'dns_nameservers':
        obj_fields.ListOfObjectsField('DNSNameServer', nullable=True),
        'host_routes':
        obj_fields.ListOfObjectsField('Route', nullable=True),
        'ipv6_ra_mode':
        common_types.IPV6ModeEnumField(nullable=True),
        'ipv6_address_mode':
        common_types.IPV6ModeEnumField(nullable=True),
        'service_types':
        obj_fields.ListOfStringsField(nullable=True)
    }

    synthetic_fields = [
        'allocation_pools', 'dns_nameservers', 'host_routes', 'service_types',
        'shared'
    ]

    foreign_keys = {'Network': {'network_id': 'id'}}

    fields_no_update = ['project_id', 'network_id', 'segment_id']

    fields_need_translation = {'host_routes': 'routes'}

    def __init__(self, context=None, **kwargs):
        super(Subnet, self).__init__(context, **kwargs)
        self.add_extra_filter_name('shared')

    def obj_load_attr(self, attrname):
        if attrname == 'shared':
            return self._load_shared()
        if attrname == 'service_types':
            return self._load_service_types()
        super(Subnet, self).obj_load_attr(attrname)

    def _load_shared(self, db_obj=None):
        if db_obj:
            # NOTE(korzen) db_obj is passed when Subnet object is loaded
            # from DB
            rbac_entries = db_obj.get('rbac_entries') or {}
            shared = (rbac_db.RbacNeutronDbObjectMixin.is_network_shared(
                self.obj_context, rbac_entries))
        else:
            # NOTE(korzen) this case is used when Subnet object was
            # instantiated and without DB interaction (get_object(s), update,
            # create), it should be rare case to load 'shared' by that method
            shared = (rbac_db.RbacNeutronDbObjectMixin.get_shared_with_tenant(
                self.obj_context.elevated(), rbac_db_models.NetworkRBAC,
                self.network_id, self.project_id))
        setattr(self, 'shared', shared)
        self.obj_reset_changes(['shared'])

    def _load_service_types(self, db_obj=None):
        if db_obj:
            service_types = db_obj.get('service_types', [])
        else:
            service_types = SubnetServiceType.get_objects(self.obj_context,
                                                          subnet_id=self.id)

        self.service_types = [
            service_type['service_type'] for service_type in service_types
        ]
        self.obj_reset_changes(['service_types'])

    def from_db_object(self, db_obj):
        super(Subnet, self).from_db_object(db_obj)
        self._load_shared(db_obj)
        self._load_service_types(db_obj)

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Subnet, cls).modify_fields_from_db(db_obj)
        if 'cidr' in result:
            result['cidr'] = utils.AuthenticIPNetwork(result['cidr'])
        if 'gateway_ip' in result and result['gateway_ip'] is not None:
            result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip'])
        return result

    @classmethod
    def modify_fields_to_db(cls, fields):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Subnet, cls).modify_fields_to_db(fields)
        if 'cidr' in result:
            result['cidr'] = cls.filter_to_str(result['cidr'])
        if 'gateway_ip' in result and result['gateway_ip'] is not None:
            result['gateway_ip'] = cls.filter_to_str(result['gateway_ip'])
        return result
Esempio n. 7
0
class FloatingIP(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = l3.FloatingIP

    fields = {
        'id': common_types.UUIDField(),
        'project_id': obj_fields.StringField(nullable=True),
        'floating_ip_address': obj_fields.IPAddressField(),
        'floating_network_id': common_types.UUIDField(),
        'floating_port_id': common_types.UUIDField(),
        'fixed_port_id': common_types.UUIDField(nullable=True),
        'fixed_ip_address': obj_fields.IPAddressField(nullable=True),
        'router_id': common_types.UUIDField(nullable=True),
        'last_known_router_id': common_types.UUIDField(nullable=True),
        'status': common_types.FloatingIPStatusEnumField(nullable=True),
        'dns': obj_fields.ObjectField('FloatingIPDNS', nullable=True),
    }
    fields_no_update = [
        'project_id', 'floating_ip_address', 'floating_network_id',
        'floating_port_id'
    ]
    synthetic_fields = ['dns']

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        result = super(FloatingIP, cls).modify_fields_from_db(db_obj)
        if 'fixed_ip_address' in result:
            result['fixed_ip_address'] = netaddr.IPAddress(
                result['fixed_ip_address'])
        if 'floating_ip_address' in result:
            result['floating_ip_address'] = netaddr.IPAddress(
                result['floating_ip_address'])
        return result

    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(FloatingIP, cls).modify_fields_to_db(fields)
        if 'fixed_ip_address' in result:
            if result['fixed_ip_address'] is not None:
                result['fixed_ip_address'] = cls.filter_to_str(
                    result['fixed_ip_address'])
        if 'floating_ip_address' in result:
            result['floating_ip_address'] = cls.filter_to_str(
                result['floating_ip_address'])
        return result

    @classmethod
    def get_scoped_floating_ips(cls, context, router_ids):
        query = context.session.query(l3.FloatingIP,
                                      models_v2.SubnetPool.address_scope_id)
        query = query.join(models_v2.Port,
                           l3.FloatingIP.fixed_port_id == models_v2.Port.id)
        # Outer join of Subnet can cause each ip to have more than one row.
        query = query.outerjoin(
            models_v2.Subnet,
            models_v2.Subnet.network_id == models_v2.Port.network_id)
        query = query.filter(models_v2.Subnet.ip_version == 4)
        query = query.outerjoin(
            models_v2.SubnetPool,
            models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)

        # Filter out on router_ids
        query = query.filter(l3.FloatingIP.router_id.in_(router_ids))
        return cls._unique_floatingip_iterator(context, query)

    @classmethod
    def _unique_floatingip_iterator(cls, context, query):
        """Iterates over only one row per floating ip. Ignores others."""
        # Group rows by fip id. They must be sorted by same.
        q = query.order_by(l3.FloatingIP.id)
        keyfunc = lambda row: row[0]['id']
        group_iterator = itertools.groupby(q, keyfunc)

        # Just hit the first row of each group
        for key, value in group_iterator:
            row = [r for r in six.next(value)]
            yield (cls._load_object(context, row[0]), row[1])
Esempio n. 8
0
class BaGPipeChainHop(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = bagpipe_db.BaGPipeChainHop

    fields = {
        'id':
        common_types.UUIDField(),
        'project_id':
        obj_fields.StringField(),
        'rts':
        obj_fields.ListOfStringsField(),
        'ingress_gw':
        obj_fields.IPAddressField(),
        'egress_gw':
        obj_fields.IPAddressField(),
        'ingress_ppg':
        common_types.UUIDField(nullable=True, default=None),
        'egress_ppg':
        common_types.UUIDField(nullable=True, default=None),
        'ingress_network':
        common_types.UUIDField(nullable=True, default=None),
        'egress_network':
        common_types.UUIDField(nullable=True, default=None),
        'ingress_ports':
        obj_fields.ListOfStringsField(nullable=True),
        'egress_ports':
        obj_fields.ListOfStringsField(nullable=True),
        'readv_from_rts':
        obj_fields.ListOfStringsField(nullable=True, default=None),
        'readv_to_rt':
        obj_fields.StringField(nullable=True, default=None),
        'attract_to_rt':
        obj_fields.StringField(nullable=True, default=None),
        'redirect_rts':
        obj_fields.ListOfStringsField(nullable=True, default=None),
        'classifiers':
        obj_fields.StringField(nullable=True, default=None),
        'reverse_hop':
        obj_fields.BooleanField(default=False),
        'portchain_id':
        common_types.UUIDField(),
    }

    fields_no_update = ['id', 'project_id']

    primary_keys = ['portchain_id']

    foreign_keys = {
        'Network': {
            'ingress_network': 'id',
            'egress_network': 'id'
        }
    }

    synthetic_fields = ['ingress_ports', 'egress_ports']

    rt_fields = {'rts', 'readv_from_rts', 'redirect_rts'}

    def from_db_object(self, db_obj):
        super(BaGPipeChainHop, self).from_db_object(db_obj)

        self._load_ingress_ports(db_obj)
        self._load_egress_ports(db_obj)

    def _load_ports_by_side(self, side):
        ports = []
        if getattr(self, side + '_ppg'):
            # pylint: disable=no-member
            reverse_side = (side if self.reverse_hop else
                            constants.REVERSE_PORT_SIDE[side])

            ports = ([
                getattr(pp, reverse_side)
                for pp in model_query.get_collection_query(
                    self.obj_context,
                    sfc_db.PortPair,
                    filters={
                        'portpairgroup_id': [getattr(self, side + '_ppg')]
                    })
            ])
        elif getattr(self, side + '_network'):
            port_objs = (Port.get_objects(self.obj_context,
                                          network_id=getattr(
                                              self, side + '_network')))
            ports = [port_obj.id for port_obj in port_objs]

        setattr(self, side + '_ports', ports)
        self.obj_reset_changes([side + '_ports'])

    def _load_ingress_ports(self, db_obj=None):
        self._load_ports_by_side(constants.INGRESS)

    def _load_egress_ports(self, db_obj=None):
        self._load_ports_by_side(constants.EGRESS)

    @staticmethod
    def _is_port_in_pp(context, port_id):
        try:
            query = context.session.query(sfc_db.PortPair)
            query = query.filter(
                sa.or_(sfc_db.PortPair.ingress == port_id,
                       sfc_db.PortPair.egress == port_id))
            query.one()
            return True
        except exc.NoResultFound:
            return False

    @staticmethod
    def _get_chain_hops_for_port_by_ppg_side(context, port_id, side):
        reverse_side = constants.REVERSE_PORT_SIDE[side]

        query = context.session.query(bagpipe_db.BaGPipeChainHop)
        query = query.join(
            sfc_db.PortPairGroup,
            sfc_db.PortPairGroup.id == getattr(bagpipe_db.BaGPipeChainHop,
                                               side + '_ppg'))
        query = query.join(
            sfc_db.PortPair,
            sfc_db.PortPair.portpairgroup_id == sfc_db.PortPairGroup.id)
        query = query.filter(
            sa.or_(
                sa.and_(
                    getattr(sfc_db.PortPair, reverse_side) == port_id,
                    bagpipe_db.BaGPipeChainHop.reverse_hop == false()),
                sa.and_(
                    getattr(sfc_db.PortPair, side) == port_id,
                    bagpipe_db.BaGPipeChainHop.reverse_hop == true())))

        return query.all()

    @staticmethod
    def _get_chain_hops_for_port_by_network_side(context, port_id, side):
        reverse_side = constants.REVERSE_PORT_SIDE[side]

        query = context.session.query(bagpipe_db.BaGPipeChainHop)
        query = query.join(
            models_v2.Network,
            sa.or_(
                sa.and_(
                    models_v2.Network.id == getattr(bagpipe_db.BaGPipeChainHop,
                                                    side + '_network'),
                    bagpipe_db.BaGPipeChainHop.reverse_hop == false()),
                sa.and_(
                    models_v2.Network.id == getattr(bagpipe_db.BaGPipeChainHop,
                                                    reverse_side + '_network'),
                    bagpipe_db.BaGPipeChainHop.reverse_hop == true())))
        query = query.join(models_v2.Port,
                           models_v2.Port.network_id == models_v2.Network.id)
        query = query.filter(models_v2.Port.id == port_id)

        return query.all()

    @classmethod
    def get_chain_hops_for_port_by_side(cls, context, port_id, side):
        db_objs = []
        if cls._is_port_in_pp(context, port_id):
            db_objs += (cls._get_chain_hops_for_port_by_ppg_side(
                context, port_id, side))
        else:
            db_objs += (cls._get_chain_hops_for_port_by_network_side(
                context, port_id, side))

        return [cls._load_object(context, db_obj) for db_obj in db_objs]

    @classmethod
    def get_objects(cls,
                    context,
                    _pager=None,
                    validate_filters=True,
                    **kwargs):
        if 'port_id' in kwargs:
            port_id = kwargs.pop('port_id')
            chain_hops = []

            for side in [constants.INGRESS, constants.EGRESS]:
                chain_hops += cls.get_chain_hops_for_port_by_side(
                    context, port_id, side)

            return chain_hops

        return super(BaGPipeChainHop,
                     cls).get_objects(context,
                                      _pager=_pager,
                                      validate_filters=validate_filters,
                                      **kwargs)

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        fields = super(BaGPipeChainHop, cls).modify_fields_from_db(db_obj)

        for field in cls.rt_fields:
            fields[field] = (fields[field].split(',')
                             if fields.get(field) else [])

        for gw_ip in ['ingress_gw', 'egress_gw']:
            if fields.get(gw_ip) is not None:
                fields[gw_ip] = netaddr.IPAddress(fields[gw_ip])

        return fields

    @classmethod
    def modify_fields_to_db(cls, fields):
        result = super(BaGPipeChainHop, cls).modify_fields_to_db(fields)

        for field in cls.rt_fields:
            if result.get(field):
                result[field] = ','.join(result[field])

        for gw_ip in ['ingress_gw', 'egress_gw']:
            if result.get(gw_ip) is not None:
                result[gw_ip] = cls.filter_to_str(result[gw_ip])

        return result
Esempio n. 9
0
class Subnet(base.NeutronDbObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    db_model = models_v2.Subnet

    fields = {
        'id':
        obj_fields.UUIDField(),
        'project_id':
        obj_fields.UUIDField(),
        'name':
        obj_fields.StringField(),
        'network_id':
        obj_fields.UUIDField(),
        'subnetpool_id':
        obj_fields.UUIDField(nullable=True),
        'ip_version':
        common_types.IPVersionEnumField(),
        'cidr':
        obj_fields.IPNetworkField(),
        'gateway_ip':
        obj_fields.IPAddressField(nullable=True),
        'allocation_pools':
        obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True),
        'enable_dhcp':
        obj_fields.BooleanField(),
        'dns_nameservers':
        obj_fields.ListOfObjectsField('DNSNameServer', nullable=True),
        'host_routes':
        obj_fields.ListOfObjectsField('Route', nullable=True),
        'ipv6_ra_mode':
        common_types.IPV6ModeEnumField(nullable=True),
        'ipv6_address_mode':
        common_types.IPV6ModeEnumField(nullable=True)
    }

    synthetic_fields = ['allocation_pools', 'dns_nameservers', 'host_routes']

    foreign_keys = {'network_id': 'id'}

    fields_need_translation = {'project_id': 'tenant_id'}

    @classmethod
    def modify_fields_from_db(cls, db_obj):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Subnet, cls).modify_fields_from_db(db_obj)
        if 'cidr' in result:
            result['cidr'] = netaddr.IPNetwork(result['cidr'])
        if 'gateway_ip' in result and result['gateway_ip'] is not None:
            result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip'])
        return result

    @classmethod
    def modify_fields_to_db(cls, fields):
        # TODO(korzen) remove this method when IP and CIDR decorator ready
        result = super(Subnet, cls).modify_fields_to_db(fields)
        if 'cidr' in result:
            result['cidr'] = str(result['cidr'])
        if 'gateway_ip' in result and result['gateway_ip'] is not None:
            result['gateway_ip'] = str(result['gateway_ip'])
        return result