def find_candidate_subnets(cls, context, network_id, host, service_type, fixed_configured, fixed_ips, distributed_service=False): """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 cls._query_filter_by_fixed_ips_segment( query, fixed_ips, allow_multiple_segments=distributed_service).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]
def _ipam_get_subnets(self, context, network_id, host): Subnet = models_v2.Subnet SegmentHostMapping = segment_svc_db.SegmentHostMapping query = self._get_collection_query(context, Subnet) query = query.filter(Subnet.network_id == network_id) # Note: This seems redundant, but its not. It has to cover cases # where host is None, ATTR_NOT_SPECIFIED, or '' due to differences in # host binding implementations. if not validators.is_attr_set(host) or not host: query = query.filter(Subnet.segment_id.is_(None)) return [self._make_subnet_dict(c, context=context) for c in query] # 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_(Subnet.segment_id == SegmentHostMapping.segment_id, SegmentHostMapping.host == host)) # Essentially "segment_id IS NULL XNOR host IS NULL" query = query.filter( or_( and_(Subnet.segment_id.isnot(None), SegmentHostMapping.host.isnot(None)), and_(Subnet.segment_id.is_(None), SegmentHostMapping.host.is_(None)))) results = query.all() # See if results are empty because the host isn't mapped to a segment if not results: # Check if it's a routed network (i.e subnets on segments) query = self._get_collection_query(context, Subnet) query = query.filter(Subnet.network_id == network_id) query = query.filter(Subnet.segment_id.isnot(None)) if query.count() == 0: return [] # It is a routed network but no subnets found for host raise segment_exc.HostNotConnectedToAnySegment( host=host, network_id=network_id) # 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 [ self._make_subnet_dict(subnet, context=context) for subnet, _mapping in results ]