def require_tenant_aggregate(ctxt, request_spec): """Require hosts in an aggregate based on tenant id. This will modify request_spec to request hosts in an aggregate defined specifically for the tenant making the request. We do that by looking for a nova host aggregate with metadata indicating which tenant it is for, and passing that aggregate uuid to placement to limit results accordingly. """ enabled = CONF.scheduler.limit_tenants_to_placement_aggregate agg_required = CONF.scheduler.placement_aggregate_required_for_tenants if not enabled: return aggregates = objects.AggregateList.get_by_metadata( ctxt, value=request_spec.project_id) aggregate_uuids_for_tenant = set([]) for agg in aggregates: for key, value in agg.metadata.items(): if key.startswith(TENANT_METADATA_KEY): aggregate_uuids_for_tenant.add(agg.uuid) break if aggregate_uuids_for_tenant: if ('requested_destination' not in request_spec or request_spec.requested_destination is None): request_spec.requested_destination = objects.Destination() destination = request_spec.requested_destination destination.require_aggregates(aggregate_uuids_for_tenant) elif agg_required: LOG.warning('Tenant %(tenant)s has no available aggregates', {'tenant': request_spec.project_id}) raise exception.RequestFilterFailed( reason=_('No hosts available for tenant'))
def routed_networks_filter( ctxt: nova_context.RequestContext, request_spec: 'objects.RequestSpec' ) -> bool: """Adds requested placement aggregates that match requested networks. This will modify request_spec to request hosts in aggregates that matches segment IDs related to requested networks. :param ctxt: The usual suspect for a context object. :param request_spec: a classic RequestSpec object containing the request. :returns: True if the filter was used or False if not. :raises: exception.InvalidRoutedNetworkConfiguration if something went wrong when trying to get the related segment aggregates. """ if not CONF.scheduler.query_placement_for_routed_network_aggregates: return False # NOTE(sbauza): On a create operation with no specific network request, we # allocate the network only after scheduling when the nova-compute service # calls Neutron. In this case, here we just want to accept any destination # as fine. # NOTE(sbauza): This could be also going from an old compute reschedule. if 'requested_networks' not in request_spec: return True # This object field is not nullable requested_networks = request_spec.requested_networks # NOTE(sbauza): This field could be not created yet. if ( 'requested_destination' not in request_spec or request_spec.requested_destination is None ): request_spec.requested_destination = objects.Destination() # Get the clients we need network_api = neutron.API() report_api = report.SchedulerReportClient() for requested_network in requested_networks: network_id = None # Check for a specifically requested network ID. if "port_id" in requested_network and requested_network.port_id: # We have to lookup the port to see which segment(s) to support. port = network_api.show_port(ctxt, requested_network.port_id)[ "port" ] if port['fixed_ips']: # The instance already exists with a related subnet. We need to # stick on this subnet. # NOTE(sbauza): In case of multiple IPs, we could have more # subnets than only one but given they would be for the same # port, just looking at the first subnet is needed. subnet_id = port['fixed_ips'][0]['subnet_id'] try: aggregates = utils.get_aggregates_for_routed_subnet( ctxt, network_api, report_api, subnet_id) except exception.InvalidRoutedNetworkConfiguration as e: raise exception.RequestFilterFailed( reason=_('Aggregates not found for the subnet %s' ) % subnet_id) from e else: # The port was just created without a subnet. network_id = port["network_id"] elif ( "network_id" in requested_network and requested_network.network_id ): network_id = requested_network.network_id if network_id: # As the user only requested a network or a port unbound to a # segment, we are free to choose any segment from the network. try: aggregates = utils.get_aggregates_for_routed_network( ctxt, network_api, report_api, network_id) except exception.InvalidRoutedNetworkConfiguration as e: raise exception.RequestFilterFailed( reason=_('Aggregates not found for the network %s' ) % network_id) from e if aggregates: LOG.debug( 'routed_networks_filter request filter added the following ' 'aggregates for network ID %s: %s', network_id, ', '.join(aggregates)) # NOTE(sbauza): All of the aggregates from this request will be # accepted, but they will have an AND relationship with any other # requested aggregate, like for another NIC request in this loop. request_spec.requested_destination.require_aggregates(aggregates) return True