def get_candidates(self, plugin, context, r_hd_binding_db):
        """Selection criteria: Hosting devices that...
               ... are based on the template required by router's type
               AND
               ... are administratively up
               AND
               ... are active (i.e., has status HD_ACTIVE)
               AND
               ... are bound to tenant owning router OR is unbound
               AND
               ... are enough slots available to host the router

            Among hosting devices meeting these criteria the device with
            less allocated slots is preferred.
        """
        # SELECT hosting_device_id, created_at, sum(num_allocated)
        # FROM hostingdevices AS hd
        # LEFT OUTER JOIN slotallocations AS sa ON hd.id=sa.hosting_device_id
        # WHERE
        #    hd.template_id='11111111-2222-3333-4444-555555555555' AND
        #    hd.admin_state_up=TRUE AND
        #    hd.status='ACTIVE' AND
        # <<<sharing case:>>>
        #    (hd.tenant_bound IS NULL OR hd.tenant_bound='t10')
        # <<<non-sharing case:>>>
        #    (sa.tenant_bound='t10' OR
        #     (sa.tenant_bound IS NULL AND sa.logical_resource_owner='t10') OR
        #     hd.tenant_bound='t10' OR
        #     (hd.tenant_bound IS NULL AND sa.hosting_device_id IS NULL))
        # GROUP BY hosting_device_id
        # HAVING sum(num_allocated) <= 8 OR sum(num_allocated) IS NULL
        # ORDER BY created_at;
        router = r_hd_binding_db.router
        tenant_id = router['tenant_id']
        router_type_db = r_hd_binding_db.router_type
        template_id = router_type_db['template_id']
        template_db = router_type_db.template
        slot_threshold = template_db.slot_capacity - router_type_db.slot_need

        query = context.session.query(
            hd_models.HostingDevice.id, hd_models.HostingDevice.created_at,
            func.sum(hd_models.SlotAllocation.num_allocated))
        query = query.outerjoin(
            hd_models.SlotAllocation,
            hd_models.HostingDevice.id ==
            hd_models.SlotAllocation.hosting_device_id)
        query = query.filter(
            hd_models.HostingDevice.template_id == template_id,
            hd_models.HostingDevice.admin_state_up == expr.true(),
            hd_models.HostingDevice.status == cisco_constants.HD_ACTIVE)
        if r_hd_binding_db.share_hosting_device:
            query = query.filter(
                expr.or_(hd_models.HostingDevice.tenant_bound == expr.null(),
                         hd_models.HostingDevice.tenant_bound == tenant_id))
        else:
            query = query.filter(
                expr.or_(
                    hd_models.SlotAllocation.tenant_bound == tenant_id,
                    expr.and_(
                        hd_models.SlotAllocation.tenant_bound == expr.null(),
                        hd_models.SlotAllocation.logical_resource_owner ==
                        tenant_id),
                    hd_models.HostingDevice.tenant_bound == tenant_id,
                    expr.and_(
                        hd_models.HostingDevice.tenant_bound == expr.null(),
                        hd_models.SlotAllocation.hosting_device_id ==
                        expr.null())))
        query = query.group_by(hd_models.HostingDevice.id)
        query = query.having(
            expr.or_(func.sum(
                hd_models.SlotAllocation.num_allocated) <= slot_threshold,
                func.sum(hd_models.SlotAllocation.num_allocated ==
                         expr.null())))
        query = query.order_by(hd_models.HostingDevice.created_at)
        return query.all()
dataset/ETHPy150Open openstack/networking-cisco/networking_cisco/plugins/cisco/l3/schedulers/l3_router_hosting_device_scheduler.py/L3RouterHostingDeviceBaseScheduler.get_candidates/original