<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="1" failures="9" skipped="3" tests="44" time="5615.575" timestamp="2026-07-14T14:46:38.249602" hostname="kserve-group-test-2tgtx-e2e-llm-inference-service-pod"><testcase classname="" name="explainer.test_art_explainer" time="0.000"><skipped message="collection skipped">('/workspace/source/test/e2e/explainer/test_art_explainer.py', 38, 'Skipped: ODH does not support art explainer at the moment')</skipped></testcase><testcase classname="" name="predictor.test_grpc" time="0.000"><skipped message="collection skipped">('/workspace/source/test/e2e/predictor/test_grpc.py', 35, 'Skipped: Not testable in ODH at the moment')</skipped></testcase><testcase classname="" name="predictor.test_torchserve" time="0.000"><skipped message="collection skipped">('/workspace/source/test/e2e/predictor/test_torchserve.py', 34, 'Skipped: ODH does not support torchserve at the moment')</skipped></testcase><testcase classname="llmisvc.test_gateway_section_name" name="test_gateway_section_name_propagation[cluster_single_node-cluster_cpu-with-section-name]" time="36.460" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-llmd-simulator2]" time="147.655" /><testcase classname="llmisvc.test_gateway_section_name" name="test_gateway_section_name_propagation[cluster_single_node-cluster_cpu-without-section-name]" time="19.969" /><testcase classname="llmisvc.test_llm_auth" name="test_llm_auth_enabled_requires_token[cluster_cpu-cluster_single_node-auth-enabled-default]" time="209.306" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-fb-opt-125m-with-lora-hf0]" time="142.105" /><testcase classname="llmisvc.test_llm_auth" name="test_llm_auth_invalid_token_rejected[cluster_cpu-cluster_single_node-auth-invalid-token]" time="145.529" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-fb-opt-125m-with-lora-hf1]" time="123.182" /><testcase classname="llmisvc.test_llm_auth" name="test_llm_auth_disabled_no_token_required[cluster_cpu-cluster_single_node-auth-disabled]" time="161.921" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-pvc]" time="224.922" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-with-gateway-ref-router-with-managed-route-model-fb-opt-125m-workload-llmd-simulator]" time="24.198" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-fb-opt-125m]" time="199.569" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-pd-cpu-model-pvc]" time="221.542" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-custom-route-timeout-scheduler-managed-workload-single-cpu-model-fb-opt-125m]" time="174.143" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_multi_node-router-managed-workload-simulated-dp-ep-cpu-model-pvc]" time="291.547" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-with-refs-scheduler-managed-workload-single-cpu-model-fb-opt-125m]" time="157.577" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-pd-cpu-model-fb-opt-125m]" time="211.643" /><testcase classname="llmisvc.test_llm_inference_service_config_deletion" name="test_config_finalizer_added" time="2.122" /><testcase classname="llmisvc.test_llm_inference_service_config_deletion" name="test_config_deletion_blocked_when_referenced" time="16.749" /><testcase classname="llmisvc.test_llm_inference_service_config_deletion" name="test_config_deletion_allowed_when_unreferenced" time="4.252" /><testcase classname="llmisvc.test_llm_inference_service_config_deletion" name="test_config_deletion_unblocked_after_service_deleted" time="2.373" /><testcase classname="llmisvc.test_llm_inference_service_config_deletion" name="test_well_known_config_deletion_prevented_by_webhook" time="0.113" /><testcase classname="llmisvc.test_llm_inference_service_config_deletion" name="test_well_known_config_deletion_blocked_by_implicit_reference" time="122.682"><failure message="AssertionError: Config kserve-config-llm-template still exists, expected 404">@pytest.mark.llminferenceservice
    @pytest.mark.cluster_cpu
    @pytest.mark.cluster_single_node
    @log_execution
    def test_well_known_config_deletion_blocked_by_implicit_reference():
        """Test that a well-known config is blocked from deletion by the finalizer while any
        LLMInferenceService exists in the same namespace.
    
        Uses a well-known-named config in the test namespace to avoid the webhook that
        prevents deletion of well-known configs in the kserve namespace.
        """
        inject_k8s_proxy()
        kserve_client = _kserve_client()
        service_name = "e2e-wk-cfg-implicit-svc"
        extra_configs = []
        llm_svc = None
    
        # Discover the well-known config name (handles custom prefixes)
        wk_config_name = _find_well_known_config(kserve_client, WELL_KNOWN_CONFIG_SUFFIX)
        assert wk_config_name is not None, (
            f"No config ending with {WELL_KNOWN_CONFIG_SUFFIX!r} found in namespace {KSERVE_NAMESPACE}"
        )
        print(f"Discovered well-known config name: {wk_config_name}")
    
        try:
            # Create a config with the well-known name in the test namespace.
            _create_config(kserve_client, wk_config_name)
            print(
                f"Created well-known config {wk_config_name} in namespace {KSERVE_TEST_NAMESPACE}"
            )
    
            # Create an LLMInferenceService that does NOT explicitly reference the
            # well-known config. The controller treats well-known configs as implicitly
            # referenced by all services in the same namespace.
            model_config_name = f"{service_name}-model-cfg"
            _create_config(kserve_client, model_config_name)
            extra_configs.append(model_config_name)
    
            llm_svc, svc_extra = _create_llmisvc_with_config_ref(
                kserve_client,
                service_name,
                model_config_name,
            )
            extra_configs.extend(svc_extra)
    
            # Wait for the well-known config to have a finalizer
            def assert_finalizer_present():
                cfg = _get_config(kserve_client, wk_config_name)
                assert _config_has_finalizer(cfg), (
                    "Finalizer not yet present on well-known config"
                )
                return True
    
            wait_for(assert_finalizer_present, timeout=60, interval=2.0)
    
            # Attempt to delete the well-known config (webhook allows it in test
            # namespace, but the finalizer should block it)
            print(
                f"Attempting to delete well-known config {wk_config_name} (should be blocked by finalizer)"
            )
            _delete_config(kserve_client, wk_config_name)
    
            # The well-known config should be blocked from deletion
            def assert_deletion_blocked():
                cfg = _get_config(kserve_client, wk_config_name)
                assert _config_has_deletion_timestamp(cfg), (
                    "Well-known config should have a deletionTimestamp after delete was called"
                )
                assert _config_has_finalizer(cfg), (
                    "Finalizer should still be present while any service exists"
                )
                in_use_cond = _get_condition(cfg, "ConfigInUse")
                assert in_use_cond is not None, "Expected ConfigInUse condition to be set"
                assert in_use_cond.get("status") == "True", (
                    f"Expected ConfigInUse=True, got {in_use_cond.get('status')}"
                )
                assert in_use_cond.get("reason") == "DeletionBlocked", (
                    f"Expected reason=DeletionBlocked, got {in_use_cond.get('reason')}"
                )
                referenced_by = cfg.get("status", {}).get("referencedBy", [])
                assert len(referenced_by) &gt; 0, (
                    f"Expected referencedBy to list services, got {referenced_by}"
                )
                return True
    
            wait_for(assert_deletion_blocked, timeout=60, interval=2.0)
            print(
                f"Well-known config {wk_config_name} deletion is correctly blocked "
                f"(ConfigInUse=True, reason=DeletionBlocked)"
            )
    
            # Delete the service to unblock
            print(f"Deleting service {service_name} to unblock well-known config deletion")
            delete_llmisvc(kserve_client, llm_svc)
            llm_svc = None
    
            # The well-known config should now be deleted
            def assert_config_gone():
                return _config_is_gone(kserve_client, wk_config_name)
    
&gt;           wait_for(assert_config_gone, timeout=120, interval=2.0)

llmisvc/test_llm_inference_service_config_deletion.py:568: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function test_well_known_config_deletion_blocked_by_implicit_reference.&lt;locals&gt;.assert_config_gone at 0x7f3e3e086700&gt;
timeout = 120, interval = 2.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def assert_config_gone():
&gt;       return _config_is_gone(kserve_client, wk_config_name)

llmisvc/test_llm_inference_service_config_deletion.py:566: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7f3e3e024110&gt;
config_name = 'kserve-config-llm-template', namespace = 'kserve-ci-e2e-test'

    def _config_is_gone(kserve_client, config_name, namespace=KSERVE_TEST_NAMESPACE):
        """Assert that a config no longer exists (404)."""
        try:
            _get_config(kserve_client, config_name, namespace)
&gt;           raise AssertionError(f"Config {config_name} still exists, expected 404")
E           AssertionError: Config kserve-config-llm-template still exists, expected 404

llmisvc/test_llm_inference_service_config_deletion.py:184: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service_conversion.TestLLMInferenceServiceConversion" name="test_v1alpha1_to_v1alpha2_conversion" time="0.633" /><testcase classname="llmisvc.test_llm_inference_service_conversion.TestLLMInferenceServiceConversion" name="test_v1alpha2_to_v1alpha1_conversion" time="0.381" /><testcase classname="llmisvc.test_llm_inference_service_conversion.TestLLMInferenceServiceConversion" name="test_criticality_preservation_via_annotations" time="0.200" /><testcase classname="llmisvc.test_llm_inference_service_conversion.TestLLMInferenceServiceConversion" name="test_lora_criticality_preservation" time="0.597" /><testcase classname="llmisvc.test_llm_inference_service_conversion.TestLLMInferenceServiceConversion" name="test_round_trip_conversion_preserves_fields" time="0.325" /><testcase classname="llmisvc.test_llm_inference_service_stop" name="test_llm_stop_feature[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-fb-opt-125m]" time="138.285" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-custom-route-timeout-pd-scheduler-managed-workload-pd-cpu-model-fb-opt-125m]" time="199.331" /><testcase classname="llmisvc.test_llm_lora_adapters" name="test_llm_with_lora_adapters[cluster_cpu-single-lora-adapter-hf]" time="404.676"><failure message="AssertionError: Expected 200 with choices, got 503: no healthy upstream&#10;assert (503 == 200)&#10; +  where 503 = &lt;Response [503]&gt;.status_code">test_case = LoRATestCase(base_refs=['router-no-scheduler', 'workload-single-cpu', 'model-fb-opt-125m-with-lora-hf'], prompt='What ...ice_name='lora-single-adapter-test', endpoint='/v1/completions', max_tokens=100, wait_timeout=900, response_timeout=60)

    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                LoRATestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    prompt="What is Kubernetes?",
                    expected_adapter_names=["lora-adapter-1"],
                    service_name="lora-single-adapter-test",
                ),
                marks=[
                    pytest.mark.llminferenceservice,
                    pytest.mark.cluster_cpu,
                    pytest.mark.lora,
                ],
                id="single-lora-adapter-hf",
            ),
            pytest.param(
                LoRATestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-multiple-lora",
                    ],
                    prompt="Explain machine learning in simple terms.",
                    expected_adapter_names=["lora-adapter-1", "lora-adapter-2"],
                    service_name="lora-multiple-adapters-test",
                ),
                marks=[
                    pytest.mark.llminferenceservice,
                    pytest.mark.cluster_cpu,
                    pytest.mark.lora,
                ],
                id="multiple-lora-adapters",
            ),
        ],
    )
    @log_execution
    def test_llm_with_lora_adapters(test_case: LoRATestCase):
        """Test LLMInferenceService with LoRA adapters."""
&gt;       run_lora_test(test_case)

llmisvc/test_llm_lora_adapters.py:252: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

test_case = LoRATestCase(base_refs=['router-no-scheduler', 'workload-single-cpu', 'model-fb-opt-125m-with-lora-hf'], prompt='What ...ice_name='lora-single-adapter-test', endpoint='/v1/completions', max_tokens=100, wait_timeout=900, response_timeout=60)

    def run_lora_test(test_case: LoRATestCase):
        """Execute a LoRA adapter test case."""
        inject_k8s_proxy()
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        test_id = generate_test_id(test_case)
        service_name = test_case.service_name or f"lora-test-{test_id}"
        model_name = _get_model_name_from_configs(test_case.base_refs)
        created_configs = []
        llm_service = None
    
        try:
            # Create unique LLMInferenceServiceConfig resources for each base ref
            unique_base_refs = []
            for base_ref in test_case.base_refs:
                if base_ref not in LLMINFERENCESERVICE_CONFIGS:
                    raise ValueError(f"Unknown base reference: {base_ref}")
    
                # Generate unique config name to avoid conflicts in parallel test runs
                unique_config_name = generate_k8s_safe_suffix(base_ref, [service_name])
                unique_base_refs.append(unique_config_name)
    
                config = LLMINFERENCESERVICE_CONFIGS[base_ref]
                config_body = {
                    "apiVersion": "serving.kserve.io/v1alpha1",
                    "kind": "LLMInferenceServiceConfig",
                    "metadata": {
                        "name": unique_config_name,
                        "namespace": KSERVE_TEST_NAMESPACE,
                    },
                    "spec": config,
                }
    
                logger.info("Creating LLMInferenceServiceConfig: %s", unique_config_name)
                _create_or_update_llmisvc_config(
                    kserve_client, config_body, KSERVE_TEST_NAMESPACE
                )
                created_configs.append(unique_config_name)
    
            # Create the service with unique base refs
            llm_service = build_llm_service_from_refs(service_name, unique_base_refs)
            if not llm_service.metadata.annotations:
                llm_service.metadata.annotations = {}
            llm_service.metadata.annotations["security.opendatahub.io/enable-auth"] = (
                "false"
            )
            logger.info("Creating LLMInferenceService: %s", service_name)
            create_llmisvc(kserve_client, llm_service)
    
            # Wait for service to be ready
            logger.info("Waiting for service %s to be ready...", service_name)
            wait_for_llm_isvc_ready(
                kserve_client, llm_service, timeout_seconds=test_case.wait_timeout
            )
    
            # Get inference URL
            base_url = get_llm_service_url(kserve_client, llm_service)
            inference_url = base_url + test_case.endpoint
    
            # Test base model inference
            logger.info("Testing base model inference...")
            base_payload = {
                "model": model_name,
                "prompt": test_case.prompt,
                "max_tokens": test_case.max_tokens,
            }
    
            base_response = post_with_retry(
                inference_url,
                json_data=base_payload,
                timeout=test_case.response_timeout,
            )
    
&gt;           assert_200_with_choices(base_response)

llmisvc/test_llm_lora_adapters.py:156: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

response = &lt;Response [503]&gt;

    def assert_200_with_choices(response: requests.Response) -&gt; None:
        """Assert 200 status code with choices in response."""
&gt;       assert (
            response.status_code == 200
            and response.json().get("choices") is not None
            and len(response.json().get("choices", [])) &gt; 0
        ), f"Expected 200 with choices, got {response.status_code}: {response.text}"
E       AssertionError: Expected 200 with choices, got 503: no healthy upstream
E       assert (503 == 200)
E        +  where 503 = &lt;Response [503]&gt;.status_code

llmisvc/test_llm_inference_service.py:55: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-with-refs-pd-scheduler-managed-workload-pd-cpu-model-fb-opt-125m]" time="206.688" /><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-no-scheduler-workload-single-cpu-model-fb-opt-125m]" time="1136.548"><failure message="AssertionError: Service returned 503: no healthy upstream">test_case = TestCase(base_refs=['router-no-scheduler', 'workload-single-cpu', 'model-fb-opt-125m'], prompt='What is KServe?', serv...              {'name': 'model-fb-opt-125m-llmisvc-model-7a2ca70d'}]},
 'status': None}, model_name='facebook/opt-125m')

    @pytest.mark.llminferenceservice
    @pytest.mark.asyncio(loop_scope="session")
    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-gateway-ref",
                        "router-with-managed-route",
                        "model-fb-opt-125m",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="custom-route-timeout-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="router-with-refs-test",
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                            routes=[ROUTER_ROUTES[0], ROUTER_ROUTES[1]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=["router-managed", "workload-pd-cpu", "model-fb-opt-125m"],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="custom-route-timeout-pd-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="router-with-refs-pd-test",
                    response_assertion=assert_200_with_choices,
                    expected_gateway=ROUTER_GATEWAYS[1],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[1]],
                            routes=[ROUTER_ROUTES[2], ROUTER_ROUTES[3]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-dp-ep-gpu",
                        "workload-dp-ep-prefill-gpu",
                        "model-deepseek-v2-lite",
                    ],
                    prompt="Delve into the multifaceted implications of a fully disaggregated cloud architecture, specifically "
                    "where the compute plane (P) and the data plane (D) are independently deployed and managed for a "
                    "geographically distributed, high-throughput, low-latency microservices ecosystem. Beyond the "
                    "fundamental challenges of network latency and data consistency, elaborate on the advanced "
                    "considerations and trade-offs inherent in such a setup: 1. Network Architecture and Protocols: "
                    "How would the network fabric and underlying protocols (e.g., RDMA, custom transport layers) need to "
                    "evolve to support optimal performance and minimize inter-plane communication overhead, especially for "
                    "synchronous operations? Discuss the role of network programmability (e.g., SDN, P4) in dynamically "
                    "optimizing routing and traffic flow between P and D. 2. Advanced Data Consistency and Durability: "
                    "Explore sophisticated data consistency models (e.g., causal consistency, strong eventual consistency) "
                    "and their applicability in balancing performance and data integrity across a globally distributed data plane. "
                    "Detail strategies for ensuring data durability and fault tolerance, including multi-region replication, "
                    "intelligent partitioning, and recovery mechanisms in the event of partial or full plane failures. "
                    "3. Dynamic Resource Orchestration and Cost Optimization: Analyze how an orchestration layer would intelligently "
                    "manage the independent scaling of compute (P) and data (D) resources, considering fluctuating workloads, "
                    "cost efficiency, and performance targets (e.g., using predictive analytics for resource provisioning). "
                    "Discuss mechanisms for dynamically reallocating compute nodes to different data partitions based on "
                    "workload patterns and data locality, potentially involving live migration strategies. "
                    "4. Security and Compliance in a Distributed Landscape: Address the enhanced security perimeter "
                    "challenges, including securing communication channels between P and D (encryption in transit, mutual TLS), "
                    "fine-grained access control to data at rest and in motion, and identity management across disaggregated "
                    "components. Discuss how such an architecture impacts compliance with regulatory frameworks (e.g., GDPR, HIPAA) "
                    "concerning data sovereignty, privacy, and auditability. 5. Operational Complexity and Observability: "
                    "Examine the increased complexity in monitoring, logging, and tracing across highly decoupled compute and "
                    "data planes. What specialized tooling and practices (e.g., distributed tracing with OpenTelemetry, advanced AIOps) "
                    "would be essential? How would incident response and troubleshooting differ in this disaggregated environment "
                    "compared to traditional integrated systems? Consider the challenges of pinpointing root causes across "
                    "independent failures. 6. Real-world Applicability and Future Trends: Identify specific industries "
                    "or use cases (e.g., high-frequency trading, IoT edge processing, large language model inference) "
                    "where the benefits of P/D disaggregation would strongly outweigh its complexities. "
                    "Conclude by speculating on emerging technologies or paradigms (e.g., serverless compute functions "
                    "directly interacting with object storage, in-memory disaggregation) that could further drive or "
                    "transform P/D disaggregation in cloud computing.",
                    max_tokens=2000,
                ),
                marks=[
                    pytest.mark.cluster_gpu,
                    pytest.mark.cluster_nvidia,
                    pytest.mark.cluster_nvidia_roce,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="What is KServe?",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.no_scheduler,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="This test simulates DP+EP that can run on CPU, the idea is to test the LWS-based deployment, "
                    "but without the resources requirements for DP+EP (GPUs and ROCe/IB).",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_multi_node],
            ),
            # Scheduler config tests
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-inline-config",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-inline-config-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Chat completions endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                        "model-qwen2.5-0.5b",
                    ],
                    model_name="Qwen/Qwen2.5-0.5B-Instruct",
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-configmap-ref",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-configmap-ref-test",
                    before_test=[create_scheduler_configmap],
                    after_test=[delete_scheduler_configmap],
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-replicas",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-ha-replicas-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-custom-template",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-custom-template-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Scheduler v0.6 → v0.7 migration tests.
            # Deploy v0.6-style configs and verify the controller migrates them
            # so the v0.7 scheduler boots successfully.
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-pd-config-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-pd-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-nonzero-threshold-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-threshold-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Precise prefix KV cache routing test
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-precise-prefix-cache-inline-config",
                        "workload-llmd-simulator-kvcache",
                    ],
                    prompt="KServe is a",
                    service_name="precise-prefix-cache-test",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Models endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/models",
                    response_assertion=create_response_assertion(with_field="data"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/completions",
                            prompt="KServe is a",
                            payload_formatter=completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/chat/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/chat/completions",
                            prompt="What is KServe?",
                            payload_formatter=chat_completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — LoRA adapter
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    model_name=f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches(
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1"
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/models (base + LoRA)
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/models",
                    response_assertion=assert_models_contains(
                        "facebook/opt-125m",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                        "lora-adapter-1",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # PVC storage tests -- validate direct PVC volume mount with real vLLM serving
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-pd-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_multi_node,
                    pytest.mark.pvc_storage,
                ],
            ),
        ],
        indirect=["test_case"],
        ids=generate_test_id,
    )
    @log_execution
    def test_llm_inference_service(test_case: TestCase):  # noqa: F811
        inject_k8s_proxy()
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        service_name = test_case.llm_service.metadata.name
        if not test_case.llm_service.metadata.annotations:
            test_case.llm_service.metadata.annotations = {}
    
        test_case.llm_service.metadata.annotations[
            "security.opendatahub.io/enable-auth"
        ] = "false"
        prefix = test_case.log_prefix
    
        test_failed = False
        try:
            print(f"{prefix} Creating LLMInferenceService {service_name}")
            create_llmisvc(kserve_client, test_case.llm_service)
            print(f"{prefix} Waiting for LLMInferenceService {service_name} to be ready")
            wait_for_llm_isvc_ready(
                kserve_client, test_case.llm_service, test_case.wait_timeout
            )
            print(f"{prefix} Waiting for model response from {service_name}")
&gt;           wait_for_model_response(
                kserve_client,
                test_case,
                test_case.wait_timeout,
                extra_headers=test_case.extra_headers,
            )

llmisvc/test_llm_inference_service.py:811: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (&lt;kserve.api.kserve_client.KServeClient object at 0x7feb3531e410&gt;, TestCase(base_refs=['router-no-scheduler', 'workloa...        {'name': 'model-fb-opt-125m-llmisvc-model-7a2ca70d'}]},
 'status': None}, model_name='facebook/opt-125m'), 900)
kwargs = {'extra_headers': None}, func_name = 'wait_for_model_response'
timestamp_start = '2026-07-14T15:16:19.241677', start_time = 1784042179.241983
duration = 1102.880018234253, timestamp_end = '2026-07-14T15:34:42.122004'

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
    
        timestamp_start = datetime.now().isoformat()
        logger.info(
            f"[{func_name}] [{timestamp_start}] start - args={args}, kwargs={kwargs}"
        )
        start_time = time.time()
    
        try:
&gt;           result = func(*args, **kwargs)

llmisvc/logging.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb3531e410&gt;
test_case = TestCase(base_refs=['router-no-scheduler', 'workload-single-cpu', 'model-fb-opt-125m'], prompt='What is KServe?', serv...              {'name': 'model-fb-opt-125m-llmisvc-model-7a2ca70d'}]},
 'status': None}, model_name='facebook/opt-125m')
timeout_seconds = 900, extra_headers = None

    @log_execution
    def wait_for_model_response(
        kserve_client: KServeClient,
        test_case: TestCase,  # noqa: F811
        timeout_seconds: int = 900,
        extra_headers: Optional[Dict[str, str]] = None,
    ) -&gt; str:
        def get_successful_response():
            try:
                if test_case.url_getter:
                    service_url = test_case.url_getter(kserve_client, test_case.llm_service)
                else:
                    service_url = get_llm_service_url(kserve_client, test_case.llm_service)
            except Exception as e:
                raise AssertionError(f"❌ Failed to get service URL: {e}") from e
    
            model_url = service_url + test_case.endpoint
    
            headers = {"Content-Type": "application/json"}
            if extra_headers:
                headers.update(extra_headers)
    
            if test_case.payload_formatter is not None:
                test_payload = test_case.payload_formatter(test_case)
            elif test_case.prompt is not None:
                test_payload = {
                    "model": test_case.model_name
                    if not extra_headers or MODEL_ROUTING_HEADER not in extra_headers
                    else extra_headers[MODEL_ROUTING_HEADER],
                    "prompt": test_case.prompt,
                    "max_tokens": test_case.max_tokens,
                }
            else:
                test_payload = None
    
            logger.info(f"Calling LLM service at {model_url} with payload {test_payload}")
            try:
                if test_payload is not None:
                    response = post_with_retry(
                        model_url,
                        headers=headers,
                        json_data=test_payload,
                        timeout=test_case.response_timeout,
                    )
                else:
                    response = get_with_retry(
                        model_url,
                        headers=headers,
                        timeout=test_case.response_timeout,
                    )
            except Exception as e:
                logger.error(f"❌ Failed to call model: {e}")
                raise AssertionError(f"❌ Failed to call model: {e}") from e
    
            logger.info(f"Model response is {response.status_code}: {response.text[:500]}")
    
            if 200 &lt;= response.status_code &lt; 300:
                return response
            raise AssertionError(
                f"Service returned {response.status_code}: {response.text}"
            )
    
&gt;       response = wait_for(get_successful_response, timeout=timeout_seconds, interval=5.0)

llmisvc/test_llm_inference_service.py:1120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function wait_for_model_response.&lt;locals&gt;.get_successful_response at 0x7feb354d87c0&gt;
timeout = 900, interval = 5.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def get_successful_response():
        try:
            if test_case.url_getter:
                service_url = test_case.url_getter(kserve_client, test_case.llm_service)
            else:
                service_url = get_llm_service_url(kserve_client, test_case.llm_service)
        except Exception as e:
            raise AssertionError(f"❌ Failed to get service URL: {e}") from e
    
        model_url = service_url + test_case.endpoint
    
        headers = {"Content-Type": "application/json"}
        if extra_headers:
            headers.update(extra_headers)
    
        if test_case.payload_formatter is not None:
            test_payload = test_case.payload_formatter(test_case)
        elif test_case.prompt is not None:
            test_payload = {
                "model": test_case.model_name
                if not extra_headers or MODEL_ROUTING_HEADER not in extra_headers
                else extra_headers[MODEL_ROUTING_HEADER],
                "prompt": test_case.prompt,
                "max_tokens": test_case.max_tokens,
            }
        else:
            test_payload = None
    
        logger.info(f"Calling LLM service at {model_url} with payload {test_payload}")
        try:
            if test_payload is not None:
                response = post_with_retry(
                    model_url,
                    headers=headers,
                    json_data=test_payload,
                    timeout=test_case.response_timeout,
                )
            else:
                response = get_with_retry(
                    model_url,
                    headers=headers,
                    timeout=test_case.response_timeout,
                )
        except Exception as e:
            logger.error(f"❌ Failed to call model: {e}")
            raise AssertionError(f"❌ Failed to call model: {e}") from e
    
        logger.info(f"Model response is {response.status_code}: {response.text[:500]}")
    
        if 200 &lt;= response.status_code &lt; 300:
            return response
&gt;       raise AssertionError(
            f"Service returned {response.status_code}: {response.text}"
        )
E       AssertionError: Service returned 503: no healthy upstream

llmisvc/test_llm_inference_service.py:1116: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_lora_adapters" name="test_llm_with_lora_adapters[cluster_cpu-multiple-lora-adapters]" time="388.788"><failure message="AssertionError: Expected 200 with choices, got 503: no healthy upstream&#10;assert (503 == 200)&#10; +  where 503 = &lt;Response [503]&gt;.status_code">test_case = LoRATestCase(base_refs=['router-no-scheduler', 'workload-single-cpu', 'model-fb-opt-125m-with-multiple-lora'], prompt=..._name='lora-multiple-adapters-test', endpoint='/v1/completions', max_tokens=100, wait_timeout=900, response_timeout=60)

    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                LoRATestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    prompt="What is Kubernetes?",
                    expected_adapter_names=["lora-adapter-1"],
                    service_name="lora-single-adapter-test",
                ),
                marks=[
                    pytest.mark.llminferenceservice,
                    pytest.mark.cluster_cpu,
                    pytest.mark.lora,
                ],
                id="single-lora-adapter-hf",
            ),
            pytest.param(
                LoRATestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-multiple-lora",
                    ],
                    prompt="Explain machine learning in simple terms.",
                    expected_adapter_names=["lora-adapter-1", "lora-adapter-2"],
                    service_name="lora-multiple-adapters-test",
                ),
                marks=[
                    pytest.mark.llminferenceservice,
                    pytest.mark.cluster_cpu,
                    pytest.mark.lora,
                ],
                id="multiple-lora-adapters",
            ),
        ],
    )
    @log_execution
    def test_llm_with_lora_adapters(test_case: LoRATestCase):
        """Test LLMInferenceService with LoRA adapters."""
&gt;       run_lora_test(test_case)

llmisvc/test_llm_lora_adapters.py:252: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

test_case = LoRATestCase(base_refs=['router-no-scheduler', 'workload-single-cpu', 'model-fb-opt-125m-with-multiple-lora'], prompt=..._name='lora-multiple-adapters-test', endpoint='/v1/completions', max_tokens=100, wait_timeout=900, response_timeout=60)

    def run_lora_test(test_case: LoRATestCase):
        """Execute a LoRA adapter test case."""
        inject_k8s_proxy()
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        test_id = generate_test_id(test_case)
        service_name = test_case.service_name or f"lora-test-{test_id}"
        model_name = _get_model_name_from_configs(test_case.base_refs)
        created_configs = []
        llm_service = None
    
        try:
            # Create unique LLMInferenceServiceConfig resources for each base ref
            unique_base_refs = []
            for base_ref in test_case.base_refs:
                if base_ref not in LLMINFERENCESERVICE_CONFIGS:
                    raise ValueError(f"Unknown base reference: {base_ref}")
    
                # Generate unique config name to avoid conflicts in parallel test runs
                unique_config_name = generate_k8s_safe_suffix(base_ref, [service_name])
                unique_base_refs.append(unique_config_name)
    
                config = LLMINFERENCESERVICE_CONFIGS[base_ref]
                config_body = {
                    "apiVersion": "serving.kserve.io/v1alpha1",
                    "kind": "LLMInferenceServiceConfig",
                    "metadata": {
                        "name": unique_config_name,
                        "namespace": KSERVE_TEST_NAMESPACE,
                    },
                    "spec": config,
                }
    
                logger.info("Creating LLMInferenceServiceConfig: %s", unique_config_name)
                _create_or_update_llmisvc_config(
                    kserve_client, config_body, KSERVE_TEST_NAMESPACE
                )
                created_configs.append(unique_config_name)
    
            # Create the service with unique base refs
            llm_service = build_llm_service_from_refs(service_name, unique_base_refs)
            if not llm_service.metadata.annotations:
                llm_service.metadata.annotations = {}
            llm_service.metadata.annotations["security.opendatahub.io/enable-auth"] = (
                "false"
            )
            logger.info("Creating LLMInferenceService: %s", service_name)
            create_llmisvc(kserve_client, llm_service)
    
            # Wait for service to be ready
            logger.info("Waiting for service %s to be ready...", service_name)
            wait_for_llm_isvc_ready(
                kserve_client, llm_service, timeout_seconds=test_case.wait_timeout
            )
    
            # Get inference URL
            base_url = get_llm_service_url(kserve_client, llm_service)
            inference_url = base_url + test_case.endpoint
    
            # Test base model inference
            logger.info("Testing base model inference...")
            base_payload = {
                "model": model_name,
                "prompt": test_case.prompt,
                "max_tokens": test_case.max_tokens,
            }
    
            base_response = post_with_retry(
                inference_url,
                json_data=base_payload,
                timeout=test_case.response_timeout,
            )
    
&gt;           assert_200_with_choices(base_response)

llmisvc/test_llm_lora_adapters.py:156: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

response = &lt;Response [503]&gt;

    def assert_200_with_choices(response: requests.Response) -&gt; None:
        """Assert 200 status code with choices in response."""
&gt;       assert (
            response.status_code == 200
            and response.json().get("choices") is not None
            and len(response.json().get("choices", [])) &gt; 0
        ), f"Expected 200 with choices, got {response.status_code}: {response.text}"
E       AssertionError: Expected 200 with choices, got 503: no healthy upstream
E       assert (503 == 200)
E        +  where 503 = &lt;Response [503]&gt;.status_code

llmisvc/test_llm_inference_service.py:55: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_tls" name="test_llm_tls_resources[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-fb-opt-125m]" time="1154.481"><failure message="AssertionError: Service returned 503: no healthy upstream">test_case = TestCase(base_refs=['router-managed', 'workload-single-cpu', 'model-fb-opt-125m'], prompt='KServe is a', service_name=...              {'name': 'model-fb-opt-125m-tls-verificat-7c5fca28'}]},
 'status': None}, model_name='facebook/opt-125m')

    @pytest.mark.llminferenceservice
    @pytest.mark.asyncio(loop_scope="session")
    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                    service_name="tls-verification-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
        ],
        indirect=["test_case"],
        ids=generate_test_id,
    )
    @log_execution
    def test_llm_tls_resources(test_case: TestCase):
        """Verify that TLS-related resources (DestinationRules, cert secrets, service port)
        are correctly present or absent based on the enableLLMInferenceServiceTLS flag."""
        inject_k8s_proxy()
    
        tls_enabled = _is_tls_enabled()
        logger.info(f"enableLLMInferenceServiceTLS = {tls_enabled}")
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        service_name = test_case.llm_service.metadata.name
        if not test_case.llm_service.metadata.annotations:
            test_case.llm_service.metadata.annotations = {}
    
        test_case.llm_service.metadata.annotations[
            "security.opendatahub.io/enable-auth"
        ] = "false"
    
        try:
            create_llmisvc(kserve_client, test_case.llm_service)
            wait_for_llm_isvc_ready(
                kserve_client, test_case.llm_service, test_case.wait_timeout
            )
&gt;           wait_for_model_response(kserve_client, test_case, test_case.wait_timeout)

llmisvc/test_llm_tls.py:143: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (&lt;kserve.api.kserve_client.KServeClient object at 0x7f3e3def7c10&gt;, TestCase(base_refs=['router-managed', 'workload-sin...        {'name': 'model-fb-opt-125m-tls-verificat-7c5fca28'}]},
 'status': None}, model_name='facebook/opt-125m'), 900)
kwargs = {}, func_name = 'wait_for_model_response'
timestamp_start = '2026-07-14T15:24:38.938784', start_time = 1784042678.93908
duration = 1102.913336277008, timestamp_end = '2026-07-14T15:43:01.852419'

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
    
        timestamp_start = datetime.now().isoformat()
        logger.info(
            f"[{func_name}] [{timestamp_start}] start - args={args}, kwargs={kwargs}"
        )
        start_time = time.time()
    
        try:
&gt;           result = func(*args, **kwargs)

llmisvc/logging.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7f3e3def7c10&gt;
test_case = TestCase(base_refs=['router-managed', 'workload-single-cpu', 'model-fb-opt-125m'], prompt='KServe is a', service_name=...              {'name': 'model-fb-opt-125m-tls-verificat-7c5fca28'}]},
 'status': None}, model_name='facebook/opt-125m')
timeout_seconds = 900, extra_headers = None

    @log_execution
    def wait_for_model_response(
        kserve_client: KServeClient,
        test_case: TestCase,  # noqa: F811
        timeout_seconds: int = 900,
        extra_headers: Optional[Dict[str, str]] = None,
    ) -&gt; str:
        def get_successful_response():
            try:
                if test_case.url_getter:
                    service_url = test_case.url_getter(kserve_client, test_case.llm_service)
                else:
                    service_url = get_llm_service_url(kserve_client, test_case.llm_service)
            except Exception as e:
                raise AssertionError(f"❌ Failed to get service URL: {e}") from e
    
            model_url = service_url + test_case.endpoint
    
            headers = {"Content-Type": "application/json"}
            if extra_headers:
                headers.update(extra_headers)
    
            if test_case.payload_formatter is not None:
                test_payload = test_case.payload_formatter(test_case)
            elif test_case.prompt is not None:
                test_payload = {
                    "model": test_case.model_name
                    if not extra_headers or MODEL_ROUTING_HEADER not in extra_headers
                    else extra_headers[MODEL_ROUTING_HEADER],
                    "prompt": test_case.prompt,
                    "max_tokens": test_case.max_tokens,
                }
            else:
                test_payload = None
    
            logger.info(f"Calling LLM service at {model_url} with payload {test_payload}")
            try:
                if test_payload is not None:
                    response = post_with_retry(
                        model_url,
                        headers=headers,
                        json_data=test_payload,
                        timeout=test_case.response_timeout,
                    )
                else:
                    response = get_with_retry(
                        model_url,
                        headers=headers,
                        timeout=test_case.response_timeout,
                    )
            except Exception as e:
                logger.error(f"❌ Failed to call model: {e}")
                raise AssertionError(f"❌ Failed to call model: {e}") from e
    
            logger.info(f"Model response is {response.status_code}: {response.text[:500]}")
    
            if 200 &lt;= response.status_code &lt; 300:
                return response
            raise AssertionError(
                f"Service returned {response.status_code}: {response.text}"
            )
    
&gt;       response = wait_for(get_successful_response, timeout=timeout_seconds, interval=5.0)

llmisvc/test_llm_inference_service.py:1120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function wait_for_model_response.&lt;locals&gt;.get_successful_response at 0x7f3e3e0cfd80&gt;
timeout = 900, interval = 5.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def get_successful_response():
        try:
            if test_case.url_getter:
                service_url = test_case.url_getter(kserve_client, test_case.llm_service)
            else:
                service_url = get_llm_service_url(kserve_client, test_case.llm_service)
        except Exception as e:
            raise AssertionError(f"❌ Failed to get service URL: {e}") from e
    
        model_url = service_url + test_case.endpoint
    
        headers = {"Content-Type": "application/json"}
        if extra_headers:
            headers.update(extra_headers)
    
        if test_case.payload_formatter is not None:
            test_payload = test_case.payload_formatter(test_case)
        elif test_case.prompt is not None:
            test_payload = {
                "model": test_case.model_name
                if not extra_headers or MODEL_ROUTING_HEADER not in extra_headers
                else extra_headers[MODEL_ROUTING_HEADER],
                "prompt": test_case.prompt,
                "max_tokens": test_case.max_tokens,
            }
        else:
            test_payload = None
    
        logger.info(f"Calling LLM service at {model_url} with payload {test_payload}")
        try:
            if test_payload is not None:
                response = post_with_retry(
                    model_url,
                    headers=headers,
                    json_data=test_payload,
                    timeout=test_case.response_timeout,
                )
            else:
                response = get_with_retry(
                    model_url,
                    headers=headers,
                    timeout=test_case.response_timeout,
                )
        except Exception as e:
            logger.error(f"❌ Failed to call model: {e}")
            raise AssertionError(f"❌ Failed to call model: {e}") from e
    
        logger.info(f"Model response is {response.status_code}: {response.text[:500]}")
    
        if 200 &lt;= response.status_code &lt; 300:
            return response
&gt;       raise AssertionError(
            f"Service returned {response.status_code}: {response.text}"
        )
E       AssertionError: Service returned 503: no healthy upstream

llmisvc/test_llm_inference_service.py:1116: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_multi_node-router-managed-workload-simulated-dp-ep-cpu-model-fb-opt-125m]" time="550.702" /><testcase classname="llmisvc.test_prestop_hook" name="test_prestop_hook[cluster_cpu-cluster_single_node-router-managed-workload-single-cpu-model-fb-opt-125m]" time="42.019" /><testcase classname="llmisvc.test_rolling_upgrade" name="test_rolling_upgrade_coordination[cluster_cpu-cluster_single_node-router-managed-workload-llmd-simulator-model-fb-opt-125m]" time="900.819"><failure message="AssertionError: Missing true conditions: {'WorkloadsReady', 'Ready'}, expected {'WorkloadsReady', 'Ready', 'RouterReady'}, got [{'lastTransitionTime': '2026-07-14T15:44:47Z', 'severity': 'Info', 'status': 'True', 'type': 'HTTPRoutesReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'severity': 'Info', 'status': 'True', 'type': 'InferencePoolReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'severity': 'Info', 'status': 'False', 'type': 'MainWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:05Z', 'severity': 'Info', 'status': 'True', 'type': 'PresetsCombined'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'Ready'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'status': 'True', 'type': 'RouterReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'severity': 'Info', 'status': 'True', 'type': 'SchedulerWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'WorkloadsReady'}]">test_case = TestCase(base_refs=['router-managed', 'workload-llmd-simulator', 'model-fb-opt-125m'], prompt='KServe is a', service_n...              {'name': 'model-fb-opt-125m-rolling-upgra-30e65219'}]},
 'status': None}, model_name='facebook/opt-125m')

    @pytest.mark.llminferenceservice
    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="rolling-upgrade-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
        ],
        indirect=["test_case"],
        ids=generate_test_id,
    )
    @log_execution
    def test_rolling_upgrade_coordination(test_case: TestCase):
        """
        Verify the service recovers cleanly after a workload rolling update.
    
        Triggers a rolling update by patching a pod-template annotation on the
        LLMInferenceService. After the rollout completes the workload Deployment
        must be Available and the overall service must be Ready and able to serve
        inference requests — confirming the workload-before-EPP update ordering
        left the system in a healthy state.
        """
        inject_k8s_proxy()
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        service_name = test_case.llm_service.metadata.name
        namespace = test_case.llm_service.metadata.namespace
        test_failed = False
    
        if not test_case.llm_service.metadata.annotations:
            test_case.llm_service.metadata.annotations = {}
        test_case.llm_service.metadata.annotations[
            "security.opendatahub.io/enable-auth"
        ] = "false"
    
        try:
            print(f"Creating LLMInferenceService {service_name}")
            create_llmisvc(kserve_client, test_case.llm_service)
    
            print(f"Waiting for {service_name} to be ready")
&gt;           wait_for_llm_isvc_ready(
                kserve_client, test_case.llm_service, test_case.wait_timeout
            )

llmisvc/test_rolling_upgrade.py:94: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (&lt;kserve.api.kserve_client.KServeClient object at 0x7f3e3e15e250&gt;, {'api_version': 'serving.kserve.io/v1alpha1',
 'kin...lling-9660aa23'},
                       {'name': 'model-fb-opt-125m-rolling-upgra-30e65219'}]},
 'status': None}, 900)
kwargs = {}, func_name = 'wait_for_llm_isvc_ready'
timestamp_start = '2026-07-14T15:43:50.954125', start_time = 1784043830.9544346
duration = 900.5611088275909, timestamp_end = '2026-07-14T15:58:51.515546'

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
    
        timestamp_start = datetime.now().isoformat()
        logger.info(
            f"[{func_name}] [{timestamp_start}] start - args={args}, kwargs={kwargs}"
        )
        start_time = time.time()
    
        try:
&gt;           result = func(*args, **kwargs)

llmisvc/logging.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7f3e3e15e250&gt;
given = {'api_version': 'serving.kserve.io/v1alpha1',
 'kind': 'LLMInferenceService',
 'metadata': {'annotations': {'security....tor-rolling-9660aa23'},
                       {'name': 'model-fb-opt-125m-rolling-upgra-30e65219'}]},
 'status': None}
timeout_seconds = 900

    @log_execution
    def wait_for_llm_isvc_ready(
        kserve_client: KServeClient,
        given: V1alpha1LLMInferenceService,
        timeout_seconds: int = 900,
    ) -&gt; str:
        def assert_llm_isvc_ready():
            out = get_llmisvc(
                kserve_client,
                given.metadata.name,
                given.metadata.namespace,
                given.api_version.split("/")[1],
            )
    
            if "status" not in out:
                raise AssertionError("No status found in LLM inference service")
    
            status = out["status"]
            if "conditions" not in status:
                raise AssertionError("No conditions found in status")
    
            expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
            got_true_conditions = set()
    
            conditions = status["conditions"]
    
            for condition in conditions:
                if condition.get("status") == "True":
                    got_true_conditions.add(condition.get("type"))
    
            missing_conditions = expected_true_conditions - got_true_conditions
            if missing_conditions:
                raise AssertionError(
                    f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
                )
            return True
    
&gt;       return wait_for(assert_llm_isvc_ready, timeout=timeout_seconds, interval=1.0)

llmisvc/test_llm_inference_service.py:1205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function wait_for_llm_isvc_ready.&lt;locals&gt;.assert_llm_isvc_ready at 0x7f3e3df794e0&gt;
timeout = 900, interval = 1.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def assert_llm_isvc_ready():
        out = get_llmisvc(
            kserve_client,
            given.metadata.name,
            given.metadata.namespace,
            given.api_version.split("/")[1],
        )
    
        if "status" not in out:
            raise AssertionError("No status found in LLM inference service")
    
        status = out["status"]
        if "conditions" not in status:
            raise AssertionError("No conditions found in status")
    
        expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
        got_true_conditions = set()
    
        conditions = status["conditions"]
    
        for condition in conditions:
            if condition.get("status") == "True":
                got_true_conditions.add(condition.get("type"))
    
        missing_conditions = expected_true_conditions - got_true_conditions
        if missing_conditions:
&gt;           raise AssertionError(
                f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
            )
E           AssertionError: Missing true conditions: {'WorkloadsReady', 'Ready'}, expected {'WorkloadsReady', 'Ready', 'RouterReady'}, got [{'lastTransitionTime': '2026-07-14T15:44:47Z', 'severity': 'Info', 'status': 'True', 'type': 'HTTPRoutesReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'severity': 'Info', 'status': 'True', 'type': 'InferencePoolReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'severity': 'Info', 'status': 'False', 'type': 'MainWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:05Z', 'severity': 'Info', 'status': 'True', 'type': 'PresetsCombined'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'Ready'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'status': 'True', 'type': 'RouterReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'severity': 'Info', 'status': 'True', 'type': 'SchedulerWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:47Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'WorkloadsReady'}]

llmisvc/test_llm_inference_service.py:1200: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-scheduler-with-inline-config-workload-llmd-simulator]" time="903.173"><failure message="AssertionError: Missing true conditions: {'WorkloadsReady', 'Ready'}, expected {'WorkloadsReady', 'RouterReady', 'Ready'}, got [{'lastTransitionTime': '2026-07-14T15:44:52Z', 'severity': 'Info', 'status': 'True', 'type': 'HTTPRoutesReady'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'severity': 'Info', 'status': 'True', 'type': 'InferencePoolReady'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'severity': 'Info', 'status': 'False', 'type': 'MainWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:20Z', 'severity': 'Info', 'status': 'True', 'type': 'PresetsCombined'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'Ready'}, {'lastTransitionTime': '2026-07-14T15:44:53Z', 'status': 'True', 'type': 'RouterReady'}, {'lastTransitionTime': '2026-07-14T15:44:53Z', 'severity': 'Info', 'status': 'True', 'type': 'SchedulerWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'WorkloadsReady'}]">test_case = TestCase(base_refs=['router-managed', 'scheduler-with-inline-config', 'workload-llmd-simulator'], prompt='KServe is a'...              {'name': 'workload-llmd-simulator-schedul-39bca5db'}]},
 'status': None}, model_name='facebook/opt-125m')

    @pytest.mark.llminferenceservice
    @pytest.mark.asyncio(loop_scope="session")
    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-gateway-ref",
                        "router-with-managed-route",
                        "model-fb-opt-125m",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="custom-route-timeout-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="router-with-refs-test",
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                            routes=[ROUTER_ROUTES[0], ROUTER_ROUTES[1]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=["router-managed", "workload-pd-cpu", "model-fb-opt-125m"],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="custom-route-timeout-pd-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="router-with-refs-pd-test",
                    response_assertion=assert_200_with_choices,
                    expected_gateway=ROUTER_GATEWAYS[1],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[1]],
                            routes=[ROUTER_ROUTES[2], ROUTER_ROUTES[3]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-dp-ep-gpu",
                        "workload-dp-ep-prefill-gpu",
                        "model-deepseek-v2-lite",
                    ],
                    prompt="Delve into the multifaceted implications of a fully disaggregated cloud architecture, specifically "
                    "where the compute plane (P) and the data plane (D) are independently deployed and managed for a "
                    "geographically distributed, high-throughput, low-latency microservices ecosystem. Beyond the "
                    "fundamental challenges of network latency and data consistency, elaborate on the advanced "
                    "considerations and trade-offs inherent in such a setup: 1. Network Architecture and Protocols: "
                    "How would the network fabric and underlying protocols (e.g., RDMA, custom transport layers) need to "
                    "evolve to support optimal performance and minimize inter-plane communication overhead, especially for "
                    "synchronous operations? Discuss the role of network programmability (e.g., SDN, P4) in dynamically "
                    "optimizing routing and traffic flow between P and D. 2. Advanced Data Consistency and Durability: "
                    "Explore sophisticated data consistency models (e.g., causal consistency, strong eventual consistency) "
                    "and their applicability in balancing performance and data integrity across a globally distributed data plane. "
                    "Detail strategies for ensuring data durability and fault tolerance, including multi-region replication, "
                    "intelligent partitioning, and recovery mechanisms in the event of partial or full plane failures. "
                    "3. Dynamic Resource Orchestration and Cost Optimization: Analyze how an orchestration layer would intelligently "
                    "manage the independent scaling of compute (P) and data (D) resources, considering fluctuating workloads, "
                    "cost efficiency, and performance targets (e.g., using predictive analytics for resource provisioning). "
                    "Discuss mechanisms for dynamically reallocating compute nodes to different data partitions based on "
                    "workload patterns and data locality, potentially involving live migration strategies. "
                    "4. Security and Compliance in a Distributed Landscape: Address the enhanced security perimeter "
                    "challenges, including securing communication channels between P and D (encryption in transit, mutual TLS), "
                    "fine-grained access control to data at rest and in motion, and identity management across disaggregated "
                    "components. Discuss how such an architecture impacts compliance with regulatory frameworks (e.g., GDPR, HIPAA) "
                    "concerning data sovereignty, privacy, and auditability. 5. Operational Complexity and Observability: "
                    "Examine the increased complexity in monitoring, logging, and tracing across highly decoupled compute and "
                    "data planes. What specialized tooling and practices (e.g., distributed tracing with OpenTelemetry, advanced AIOps) "
                    "would be essential? How would incident response and troubleshooting differ in this disaggregated environment "
                    "compared to traditional integrated systems? Consider the challenges of pinpointing root causes across "
                    "independent failures. 6. Real-world Applicability and Future Trends: Identify specific industries "
                    "or use cases (e.g., high-frequency trading, IoT edge processing, large language model inference) "
                    "where the benefits of P/D disaggregation would strongly outweigh its complexities. "
                    "Conclude by speculating on emerging technologies or paradigms (e.g., serverless compute functions "
                    "directly interacting with object storage, in-memory disaggregation) that could further drive or "
                    "transform P/D disaggregation in cloud computing.",
                    max_tokens=2000,
                ),
                marks=[
                    pytest.mark.cluster_gpu,
                    pytest.mark.cluster_nvidia,
                    pytest.mark.cluster_nvidia_roce,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="What is KServe?",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.no_scheduler,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="This test simulates DP+EP that can run on CPU, the idea is to test the LWS-based deployment, "
                    "but without the resources requirements for DP+EP (GPUs and ROCe/IB).",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_multi_node],
            ),
            # Scheduler config tests
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-inline-config",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-inline-config-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Chat completions endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                        "model-qwen2.5-0.5b",
                    ],
                    model_name="Qwen/Qwen2.5-0.5B-Instruct",
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-configmap-ref",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-configmap-ref-test",
                    before_test=[create_scheduler_configmap],
                    after_test=[delete_scheduler_configmap],
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-replicas",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-ha-replicas-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-custom-template",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-custom-template-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Scheduler v0.6 → v0.7 migration tests.
            # Deploy v0.6-style configs and verify the controller migrates them
            # so the v0.7 scheduler boots successfully.
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-pd-config-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-pd-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-nonzero-threshold-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-threshold-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Precise prefix KV cache routing test
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-precise-prefix-cache-inline-config",
                        "workload-llmd-simulator-kvcache",
                    ],
                    prompt="KServe is a",
                    service_name="precise-prefix-cache-test",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Models endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/models",
                    response_assertion=create_response_assertion(with_field="data"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/completions",
                            prompt="KServe is a",
                            payload_formatter=completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/chat/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/chat/completions",
                            prompt="What is KServe?",
                            payload_formatter=chat_completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — LoRA adapter
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    model_name=f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches(
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1"
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/models (base + LoRA)
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/models",
                    response_assertion=assert_models_contains(
                        "facebook/opt-125m",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                        "lora-adapter-1",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # PVC storage tests -- validate direct PVC volume mount with real vLLM serving
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-pd-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_multi_node,
                    pytest.mark.pvc_storage,
                ],
            ),
        ],
        indirect=["test_case"],
        ids=generate_test_id,
    )
    @log_execution
    def test_llm_inference_service(test_case: TestCase):  # noqa: F811
        inject_k8s_proxy()
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        service_name = test_case.llm_service.metadata.name
        if not test_case.llm_service.metadata.annotations:
            test_case.llm_service.metadata.annotations = {}
    
        test_case.llm_service.metadata.annotations[
            "security.opendatahub.io/enable-auth"
        ] = "false"
        prefix = test_case.log_prefix
    
        test_failed = False
        try:
            print(f"{prefix} Creating LLMInferenceService {service_name}")
            create_llmisvc(kserve_client, test_case.llm_service)
            print(f"{prefix} Waiting for LLMInferenceService {service_name} to be ready")
&gt;           wait_for_llm_isvc_ready(
                kserve_client, test_case.llm_service, test_case.wait_timeout
            )

llmisvc/test_llm_inference_service.py:807: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (&lt;kserve.api.kserve_client.KServeClient object at 0x7feb3511c210&gt;, {'api_version': 'serving.kserve.io/v1alpha1',
 'kin...ig-sc-9914b4fd'},
                       {'name': 'workload-llmd-simulator-schedul-39bca5db'}]},
 'status': None}, 900)
kwargs = {}, func_name = 'wait_for_llm_isvc_ready'
timestamp_start = '2026-07-14T15:43:55.432507', start_time = 1784043835.4327602
duration = 900.6432683467865, timestamp_end = '2026-07-14T15:58:56.076046'

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
    
        timestamp_start = datetime.now().isoformat()
        logger.info(
            f"[{func_name}] [{timestamp_start}] start - args={args}, kwargs={kwargs}"
        )
        start_time = time.time()
    
        try:
&gt;           result = func(*args, **kwargs)

llmisvc/logging.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb3511c210&gt;
given = {'api_version': 'serving.kserve.io/v1alpha1',
 'kind': 'LLMInferenceService',
 'metadata': {'annotations': {'security....e-config-sc-9914b4fd'},
                       {'name': 'workload-llmd-simulator-schedul-39bca5db'}]},
 'status': None}
timeout_seconds = 900

    @log_execution
    def wait_for_llm_isvc_ready(
        kserve_client: KServeClient,
        given: V1alpha1LLMInferenceService,
        timeout_seconds: int = 900,
    ) -&gt; str:
        def assert_llm_isvc_ready():
            out = get_llmisvc(
                kserve_client,
                given.metadata.name,
                given.metadata.namespace,
                given.api_version.split("/")[1],
            )
    
            if "status" not in out:
                raise AssertionError("No status found in LLM inference service")
    
            status = out["status"]
            if "conditions" not in status:
                raise AssertionError("No conditions found in status")
    
            expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
            got_true_conditions = set()
    
            conditions = status["conditions"]
    
            for condition in conditions:
                if condition.get("status") == "True":
                    got_true_conditions.add(condition.get("type"))
    
            missing_conditions = expected_true_conditions - got_true_conditions
            if missing_conditions:
                raise AssertionError(
                    f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
                )
            return True
    
&gt;       return wait_for(assert_llm_isvc_ready, timeout=timeout_seconds, interval=1.0)

llmisvc/test_llm_inference_service.py:1205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function wait_for_llm_isvc_ready.&lt;locals&gt;.assert_llm_isvc_ready at 0x7feb354d9f80&gt;
timeout = 900, interval = 1.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def assert_llm_isvc_ready():
        out = get_llmisvc(
            kserve_client,
            given.metadata.name,
            given.metadata.namespace,
            given.api_version.split("/")[1],
        )
    
        if "status" not in out:
            raise AssertionError("No status found in LLM inference service")
    
        status = out["status"]
        if "conditions" not in status:
            raise AssertionError("No conditions found in status")
    
        expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
        got_true_conditions = set()
    
        conditions = status["conditions"]
    
        for condition in conditions:
            if condition.get("status") == "True":
                got_true_conditions.add(condition.get("type"))
    
        missing_conditions = expected_true_conditions - got_true_conditions
        if missing_conditions:
&gt;           raise AssertionError(
                f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
            )
E           AssertionError: Missing true conditions: {'WorkloadsReady', 'Ready'}, expected {'WorkloadsReady', 'RouterReady', 'Ready'}, got [{'lastTransitionTime': '2026-07-14T15:44:52Z', 'severity': 'Info', 'status': 'True', 'type': 'HTTPRoutesReady'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'severity': 'Info', 'status': 'True', 'type': 'InferencePoolReady'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'severity': 'Info', 'status': 'False', 'type': 'MainWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:20Z', 'severity': 'Info', 'status': 'True', 'type': 'PresetsCombined'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'Ready'}, {'lastTransitionTime': '2026-07-14T15:44:53Z', 'status': 'True', 'type': 'RouterReady'}, {'lastTransitionTime': '2026-07-14T15:44:53Z', 'severity': 'Info', 'status': 'True', 'type': 'SchedulerWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:44:52Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'WorkloadsReady'}]

llmisvc/test_llm_inference_service.py:1200: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-workload-llmd-simulator-model-qwen2.5-0.5b]" time="902.450"><failure message="AssertionError: Missing true conditions: {'WorkloadsReady', 'Ready'}, expected {'WorkloadsReady', 'RouterReady', 'Ready'}, got [{'lastTransitionTime': '2026-07-14T15:59:20Z', 'severity': 'Info', 'status': 'True', 'type': 'HTTPRoutesReady'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'severity': 'Info', 'status': 'True', 'type': 'InferencePoolReady'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'severity': 'Info', 'status': 'False', 'type': 'MainWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:59:02Z', 'severity': 'Info', 'status': 'True', 'type': 'PresetsCombined'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'Ready'}, {'lastTransitionTime': '2026-07-14T15:59:32Z', 'status': 'True', 'type': 'RouterReady'}, {'lastTransitionTime': '2026-07-14T15:59:32Z', 'severity': 'Info', 'status': 'True', 'type': 'SchedulerWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'WorkloadsReady'}]">test_case = TestCase(base_refs=['router-managed', 'workload-llmd-simulator', 'model-qwen2.5-0.5b'], prompt='What is KServe?', serv...     {'name': 'model-qwen2-5-0-5b-llmisvc-mode-8ec1f6c6'}]},
 'status': None}, model_name='Qwen/Qwen2.5-0.5B-Instruct')

    @pytest.mark.llminferenceservice
    @pytest.mark.asyncio(loop_scope="session")
    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-gateway-ref",
                        "router-with-managed-route",
                        "model-fb-opt-125m",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="custom-route-timeout-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="router-with-refs-test",
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                            routes=[ROUTER_ROUTES[0], ROUTER_ROUTES[1]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=["router-managed", "workload-pd-cpu", "model-fb-opt-125m"],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="custom-route-timeout-pd-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="router-with-refs-pd-test",
                    response_assertion=assert_200_with_choices,
                    expected_gateway=ROUTER_GATEWAYS[1],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[1]],
                            routes=[ROUTER_ROUTES[2], ROUTER_ROUTES[3]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-dp-ep-gpu",
                        "workload-dp-ep-prefill-gpu",
                        "model-deepseek-v2-lite",
                    ],
                    prompt="Delve into the multifaceted implications of a fully disaggregated cloud architecture, specifically "
                    "where the compute plane (P) and the data plane (D) are independently deployed and managed for a "
                    "geographically distributed, high-throughput, low-latency microservices ecosystem. Beyond the "
                    "fundamental challenges of network latency and data consistency, elaborate on the advanced "
                    "considerations and trade-offs inherent in such a setup: 1. Network Architecture and Protocols: "
                    "How would the network fabric and underlying protocols (e.g., RDMA, custom transport layers) need to "
                    "evolve to support optimal performance and minimize inter-plane communication overhead, especially for "
                    "synchronous operations? Discuss the role of network programmability (e.g., SDN, P4) in dynamically "
                    "optimizing routing and traffic flow between P and D. 2. Advanced Data Consistency and Durability: "
                    "Explore sophisticated data consistency models (e.g., causal consistency, strong eventual consistency) "
                    "and their applicability in balancing performance and data integrity across a globally distributed data plane. "
                    "Detail strategies for ensuring data durability and fault tolerance, including multi-region replication, "
                    "intelligent partitioning, and recovery mechanisms in the event of partial or full plane failures. "
                    "3. Dynamic Resource Orchestration and Cost Optimization: Analyze how an orchestration layer would intelligently "
                    "manage the independent scaling of compute (P) and data (D) resources, considering fluctuating workloads, "
                    "cost efficiency, and performance targets (e.g., using predictive analytics for resource provisioning). "
                    "Discuss mechanisms for dynamically reallocating compute nodes to different data partitions based on "
                    "workload patterns and data locality, potentially involving live migration strategies. "
                    "4. Security and Compliance in a Distributed Landscape: Address the enhanced security perimeter "
                    "challenges, including securing communication channels between P and D (encryption in transit, mutual TLS), "
                    "fine-grained access control to data at rest and in motion, and identity management across disaggregated "
                    "components. Discuss how such an architecture impacts compliance with regulatory frameworks (e.g., GDPR, HIPAA) "
                    "concerning data sovereignty, privacy, and auditability. 5. Operational Complexity and Observability: "
                    "Examine the increased complexity in monitoring, logging, and tracing across highly decoupled compute and "
                    "data planes. What specialized tooling and practices (e.g., distributed tracing with OpenTelemetry, advanced AIOps) "
                    "would be essential? How would incident response and troubleshooting differ in this disaggregated environment "
                    "compared to traditional integrated systems? Consider the challenges of pinpointing root causes across "
                    "independent failures. 6. Real-world Applicability and Future Trends: Identify specific industries "
                    "or use cases (e.g., high-frequency trading, IoT edge processing, large language model inference) "
                    "where the benefits of P/D disaggregation would strongly outweigh its complexities. "
                    "Conclude by speculating on emerging technologies or paradigms (e.g., serverless compute functions "
                    "directly interacting with object storage, in-memory disaggregation) that could further drive or "
                    "transform P/D disaggregation in cloud computing.",
                    max_tokens=2000,
                ),
                marks=[
                    pytest.mark.cluster_gpu,
                    pytest.mark.cluster_nvidia,
                    pytest.mark.cluster_nvidia_roce,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="What is KServe?",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.no_scheduler,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="This test simulates DP+EP that can run on CPU, the idea is to test the LWS-based deployment, "
                    "but without the resources requirements for DP+EP (GPUs and ROCe/IB).",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_multi_node],
            ),
            # Scheduler config tests
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-inline-config",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-inline-config-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Chat completions endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                        "model-qwen2.5-0.5b",
                    ],
                    model_name="Qwen/Qwen2.5-0.5B-Instruct",
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-configmap-ref",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-configmap-ref-test",
                    before_test=[create_scheduler_configmap],
                    after_test=[delete_scheduler_configmap],
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-replicas",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-ha-replicas-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-custom-template",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-custom-template-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Scheduler v0.6 → v0.7 migration tests.
            # Deploy v0.6-style configs and verify the controller migrates them
            # so the v0.7 scheduler boots successfully.
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-pd-config-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-pd-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-nonzero-threshold-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-threshold-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Precise prefix KV cache routing test
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-precise-prefix-cache-inline-config",
                        "workload-llmd-simulator-kvcache",
                    ],
                    prompt="KServe is a",
                    service_name="precise-prefix-cache-test",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Models endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/models",
                    response_assertion=create_response_assertion(with_field="data"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/completions",
                            prompt="KServe is a",
                            payload_formatter=completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/chat/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/chat/completions",
                            prompt="What is KServe?",
                            payload_formatter=chat_completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — LoRA adapter
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    model_name=f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches(
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1"
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/models (base + LoRA)
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/models",
                    response_assertion=assert_models_contains(
                        "facebook/opt-125m",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                        "lora-adapter-1",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # PVC storage tests -- validate direct PVC volume mount with real vLLM serving
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-pd-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_multi_node,
                    pytest.mark.pvc_storage,
                ],
            ),
        ],
        indirect=["test_case"],
        ids=generate_test_id,
    )
    @log_execution
    def test_llm_inference_service(test_case: TestCase):  # noqa: F811
        inject_k8s_proxy()
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        service_name = test_case.llm_service.metadata.name
        if not test_case.llm_service.metadata.annotations:
            test_case.llm_service.metadata.annotations = {}
    
        test_case.llm_service.metadata.annotations[
            "security.opendatahub.io/enable-auth"
        ] = "false"
        prefix = test_case.log_prefix
    
        test_failed = False
        try:
            print(f"{prefix} Creating LLMInferenceService {service_name}")
            create_llmisvc(kserve_client, test_case.llm_service)
            print(f"{prefix} Waiting for LLMInferenceService {service_name} to be ready")
&gt;           wait_for_llm_isvc_ready(
                kserve_client, test_case.llm_service, test_case.wait_timeout
            )

llmisvc/test_llm_inference_service.py:807: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (&lt;kserve.api.kserve_client.KServeClient object at 0x7feb3549c950&gt;, {'api_version': 'serving.kserve.io/v1alpha1',
 'kin...misvc-947004c3'},
                       {'name': 'model-qwen2-5-0-5b-llmisvc-mode-8ec1f6c6'}]},
 'status': None}, 900)
kwargs = {}, func_name = 'wait_for_llm_isvc_ready'
timestamp_start = '2026-07-14T15:58:58.146504', start_time = 1784044738.14681
duration = 900.3308358192444, timestamp_end = '2026-07-14T16:13:58.477650'

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
    
        timestamp_start = datetime.now().isoformat()
        logger.info(
            f"[{func_name}] [{timestamp_start}] start - args={args}, kwargs={kwargs}"
        )
        start_time = time.time()
    
        try:
&gt;           result = func(*args, **kwargs)

llmisvc/logging.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb3549c950&gt;
given = {'api_version': 'serving.kserve.io/v1alpha1',
 'kind': 'LLMInferenceService',
 'metadata': {'annotations': {'security....tor-llmisvc-947004c3'},
                       {'name': 'model-qwen2-5-0-5b-llmisvc-mode-8ec1f6c6'}]},
 'status': None}
timeout_seconds = 900

    @log_execution
    def wait_for_llm_isvc_ready(
        kserve_client: KServeClient,
        given: V1alpha1LLMInferenceService,
        timeout_seconds: int = 900,
    ) -&gt; str:
        def assert_llm_isvc_ready():
            out = get_llmisvc(
                kserve_client,
                given.metadata.name,
                given.metadata.namespace,
                given.api_version.split("/")[1],
            )
    
            if "status" not in out:
                raise AssertionError("No status found in LLM inference service")
    
            status = out["status"]
            if "conditions" not in status:
                raise AssertionError("No conditions found in status")
    
            expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
            got_true_conditions = set()
    
            conditions = status["conditions"]
    
            for condition in conditions:
                if condition.get("status") == "True":
                    got_true_conditions.add(condition.get("type"))
    
            missing_conditions = expected_true_conditions - got_true_conditions
            if missing_conditions:
                raise AssertionError(
                    f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
                )
            return True
    
&gt;       return wait_for(assert_llm_isvc_ready, timeout=timeout_seconds, interval=1.0)

llmisvc/test_llm_inference_service.py:1205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function wait_for_llm_isvc_ready.&lt;locals&gt;.assert_llm_isvc_ready at 0x7feb354dbba0&gt;
timeout = 900, interval = 1.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def assert_llm_isvc_ready():
        out = get_llmisvc(
            kserve_client,
            given.metadata.name,
            given.metadata.namespace,
            given.api_version.split("/")[1],
        )
    
        if "status" not in out:
            raise AssertionError("No status found in LLM inference service")
    
        status = out["status"]
        if "conditions" not in status:
            raise AssertionError("No conditions found in status")
    
        expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
        got_true_conditions = set()
    
        conditions = status["conditions"]
    
        for condition in conditions:
            if condition.get("status") == "True":
                got_true_conditions.add(condition.get("type"))
    
        missing_conditions = expected_true_conditions - got_true_conditions
        if missing_conditions:
&gt;           raise AssertionError(
                f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
            )
E           AssertionError: Missing true conditions: {'WorkloadsReady', 'Ready'}, expected {'WorkloadsReady', 'RouterReady', 'Ready'}, got [{'lastTransitionTime': '2026-07-14T15:59:20Z', 'severity': 'Info', 'status': 'True', 'type': 'HTTPRoutesReady'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'severity': 'Info', 'status': 'True', 'type': 'InferencePoolReady'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'severity': 'Info', 'status': 'False', 'type': 'MainWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:59:02Z', 'severity': 'Info', 'status': 'True', 'type': 'PresetsCombined'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'Ready'}, {'lastTransitionTime': '2026-07-14T15:59:32Z', 'status': 'True', 'type': 'RouterReady'}, {'lastTransitionTime': '2026-07-14T15:59:32Z', 'severity': 'Info', 'status': 'True', 'type': 'SchedulerWorkloadReady'}, {'lastTransitionTime': '2026-07-14T15:59:20Z', 'message': 'Deployment does not have minimum availability.', 'reason': 'MinimumReplicasUnavailable', 'status': 'False', 'type': 'WorkloadsReady'}]

llmisvc/test_llm_inference_service.py:1200: AssertionError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-scheduler-with-configmap-ref-workload-llmd-simulator]" time="372.664"><failure message="RuntimeError: ❌ Exception when calling CustomObjectsApi-&gt;get_namespaced_custom_object for LLMInferenceService: (500)&#10;Reason: Internal Server Error&#10;HTTP response headers: HTTPHeaderDict({'Audit-Id': '0f8a21c4-7317-45e3-a15e-c82447ad1cc0', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'X-Kubernetes-Pf-Flowschema-Uid': '2a999f06-9419-439a-ad27-f84423daf280', 'X-Kubernetes-Pf-Prioritylevel-Uid': '7646e888-06ea-42cb-b71d-ec0f1b0b2888', 'Date': 'Tue, 14 Jul 2026 16:20:11 GMT', 'Content-Length': '292'})&#10;HTTP response body: {&quot;kind&quot;:&quot;Status&quot;,&quot;apiVersion&quot;:&quot;v1&quot;,&quot;metadata&quot;:{},&quot;status&quot;:&quot;Failure&quot;,&quot;message&quot;:&quot;conversion webhook for serving.kserve.io/v1alpha2, Kind=LLMInferenceService failed: Post \&quot;https://llmisvc-webhook-server-service.kserve.svc:443/convert?timeout=30s\&quot;: net/http: TLS handshake timeout&quot;,&quot;code&quot;:500}">kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb353aab90&gt;
name = 'scheduler-configmap-ref-test', namespace = 'kserve-ci-e2e-test'
version = 'v1alpha1'

    def get_llmisvc(
        kserve_client: KServeClient,
        name,
        namespace,
        version=constants.KSERVE_V1ALPHA1_VERSION,
    ):
        try:
&gt;           return kserve_client.api_instance.get_namespaced_custom_object(
                constants.KSERVE_GROUP,
                version,
                namespace,
                KSERVE_PLURAL_LLMINFERENCESERVICE,
                name,
            )

llmisvc/test_llm_inference_service.py:1044: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api.custom_objects_api.CustomObjectsApi object at 0x7feb3533c590&gt;
group = 'serving.kserve.io', version = 'v1alpha1'
namespace = 'kserve-ci-e2e-test', plural = 'llminferenceservices'
name = 'scheduler-configmap-ref-test', kwargs = {'_return_http_data_only': True}

    def get_namespaced_custom_object(self, group, version, namespace, plural, name, **kwargs):  # noqa: E501
        """get_namespaced_custom_object  # noqa: E501
    
        Returns a namespace scoped custom object  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        &gt;&gt;&gt; thread = api.get_namespaced_custom_object(group, version, namespace, plural, name, async_req=True)
        &gt;&gt;&gt; result = thread.get()
    
        :param async_req bool: execute request asynchronously
        :param str group: the custom resource's group (required)
        :param str version: the custom resource's version (required)
        :param str namespace: The custom resource's namespace (required)
        :param str plural: the custom resource's plural name. For TPRs this would be lowercase plural kind. (required)
        :param str name: the custom object's name (required)
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return: object
                 If the method is called asynchronously,
                 returns the request thread.
        """
        kwargs['_return_http_data_only'] = True
&gt;       return self.get_namespaced_custom_object_with_http_info(group, version, namespace, plural, name, **kwargs)  # noqa: E501

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api/custom_objects_api.py:1632: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api.custom_objects_api.CustomObjectsApi object at 0x7feb3533c590&gt;
group = 'serving.kserve.io', version = 'v1alpha1'
namespace = 'kserve-ci-e2e-test', plural = 'llminferenceservices'
name = 'scheduler-configmap-ref-test', kwargs = {'_return_http_data_only': True}
local_var_params = {'_return_http_data_only': True, 'all_params': ['group', 'version', 'namespace', 'plural', 'name', 'async_req', ...], 'auth_settings': ['BearerToken'], 'body_params': None, ...}
all_params = ['group', 'version', 'namespace', 'plural', 'name', 'async_req', ...]
key = '_return_http_data_only', val = True, collection_formats = {}
path_params = {'group': 'serving.kserve.io', 'name': 'scheduler-configmap-ref-test', 'namespace': 'kserve-ci-e2e-test', 'plural': 'llminferenceservices', ...}
query_params = []

    def get_namespaced_custom_object_with_http_info(self, group, version, namespace, plural, name, **kwargs):  # noqa: E501
        """get_namespaced_custom_object  # noqa: E501
    
        Returns a namespace scoped custom object  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        &gt;&gt;&gt; thread = api.get_namespaced_custom_object_with_http_info(group, version, namespace, plural, name, async_req=True)
        &gt;&gt;&gt; result = thread.get()
    
        :param async_req bool: execute request asynchronously
        :param str group: the custom resource's group (required)
        :param str version: the custom resource's version (required)
        :param str namespace: The custom resource's namespace (required)
        :param str plural: the custom resource's plural name. For TPRs this would be lowercase plural kind. (required)
        :param str name: the custom object's name (required)
        :param _return_http_data_only: response data without head status code
                                       and headers
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return: tuple(object, status_code(int), headers(HTTPHeaderDict))
                 If the method is called asynchronously,
                 returns the request thread.
        """
    
        local_var_params = locals()
    
        all_params = [
            'group',
            'version',
            'namespace',
            'plural',
            'name'
        ]
        all_params.extend(
            [
                'async_req',
                '_return_http_data_only',
                '_preload_content',
                '_request_timeout'
            ]
        )
    
        for key, val in six.iteritems(local_var_params['kwargs']):
            if key not in all_params:
                raise ApiTypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method get_namespaced_custom_object" % key
                )
            local_var_params[key] = val
        del local_var_params['kwargs']
        # verify the required parameter 'group' is set
        if self.api_client.client_side_validation and ('group' not in local_var_params or  # noqa: E501
                                                        local_var_params['group'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `group` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'version' is set
        if self.api_client.client_side_validation and ('version' not in local_var_params or  # noqa: E501
                                                        local_var_params['version'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `version` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'namespace' is set
        if self.api_client.client_side_validation and ('namespace' not in local_var_params or  # noqa: E501
                                                        local_var_params['namespace'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `namespace` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'plural' is set
        if self.api_client.client_side_validation and ('plural' not in local_var_params or  # noqa: E501
                                                        local_var_params['plural'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `plural` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'name' is set
        if self.api_client.client_side_validation and ('name' not in local_var_params or  # noqa: E501
                                                        local_var_params['name'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `name` when calling `get_namespaced_custom_object`")  # noqa: E501
    
        collection_formats = {}
    
        path_params = {}
        if 'group' in local_var_params:
            path_params['group'] = local_var_params['group']  # noqa: E501
        if 'version' in local_var_params:
            path_params['version'] = local_var_params['version']  # noqa: E501
        if 'namespace' in local_var_params:
            path_params['namespace'] = local_var_params['namespace']  # noqa: E501
        if 'plural' in local_var_params:
            path_params['plural'] = local_var_params['plural']  # noqa: E501
        if 'name' in local_var_params:
            path_params['name'] = local_var_params['name']  # noqa: E501
    
        query_params = []
    
        header_params = {}
    
        form_params = []
        local_var_files = {}
    
        body_params = None
        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.select_header_accept(
            ['application/json'])  # noqa: E501
    
        # Authentication setting
        auth_settings = ['BearerToken']  # noqa: E501
    
&gt;       return self.api_client.call_api(
            '/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}', 'GET',
            path_params,
            query_params,
            header_params,
            body=body_params,
            post_params=form_params,
            files=local_var_files,
            response_type='object',  # noqa: E501
            auth_settings=auth_settings,
            async_req=local_var_params.get('async_req'),
            _return_http_data_only=local_var_params.get('_return_http_data_only'),  # noqa: E501
            _preload_content=local_var_params.get('_preload_content', True),
            _request_timeout=local_var_params.get('_request_timeout'),
            collection_formats=collection_formats)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api/custom_objects_api.py:1739: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3533f110&gt;
resource_path = '/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}'
method = 'GET'
path_params = {'group': 'serving.kserve.io', 'name': 'scheduler-configmap-ref-test', 'namespace': 'kserve-ci-e2e-test', 'plural': 'llminferenceservices', ...}
query_params = []
header_params = {'Accept': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = None, post_params = [], files = {}, response_type = 'object'
auth_settings = ['BearerToken'], async_req = None, _return_http_data_only = True
collection_formats = {}, _preload_content = True, _request_timeout = None
_host = None

    def call_api(self, resource_path, method,
                 path_params=None, query_params=None, header_params=None,
                 body=None, post_params=None, files=None,
                 response_type=None, auth_settings=None, async_req=None,
                 _return_http_data_only=None, collection_formats=None,
                 _preload_content=True, _request_timeout=None, _host=None):
        """Makes the HTTP request (synchronous) and returns deserialized data.
    
        To make an async_req request, set the async_req parameter.
    
        :param resource_path: Path to method endpoint.
        :param method: Method to call.
        :param path_params: Path parameters in the url.
        :param query_params: Query parameters in the url.
        :param header_params: Header parameters to be
            placed in the request header.
        :param body: Request body.
        :param post_params dict: Request post form parameters,
            for `application/x-www-form-urlencoded`, `multipart/form-data`.
        :param auth_settings list: Auth Settings names for the request.
        :param response: Response data type.
        :param files dict: key -&gt; filename, value -&gt; filepath,
            for `multipart/form-data`.
        :param async_req bool: execute request asynchronously
        :param _return_http_data_only: response data without head status code
                                       and headers
        :param collection_formats: dict of collection formats for path, query,
            header, and post parameters.
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return:
            If async_req parameter is True,
            the request will be called asynchronously.
            The method will return the request thread.
            If parameter async_req is False or missing,
            then the method will return the response directly.
        """
        if not async_req:
&gt;           return self.__call_api(resource_path, method,
                                   path_params, query_params, header_params,
                                   body, post_params, files,
                                   response_type, auth_settings,
                                   _return_http_data_only, collection_formats,
                                   _preload_content, _request_timeout, _host)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:348: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3533f110&gt;
resource_path = '/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceservices/scheduler-configmap-ref-test'
method = 'GET'
path_params = [('group', 'serving.kserve.io'), ('version', 'v1alpha1'), ('namespace', 'kserve-ci-e2e-test'), ('plural', 'llminferenceservices'), ('name', 'scheduler-configmap-ref-test')]
query_params = []
header_params = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = None, post_params = [], files = {}, response_type = 'object'
auth_settings = ['BearerToken'], _return_http_data_only = True
collection_formats = {}, _preload_content = True, _request_timeout = None
_host = None

    def __call_api(
            self, resource_path, method, path_params=None,
            query_params=None, header_params=None, body=None, post_params=None,
            files=None, response_type=None, auth_settings=None,
            _return_http_data_only=None, collection_formats=None,
            _preload_content=True, _request_timeout=None, _host=None):
    
        config = self.configuration
    
        # header parameters
        header_params = header_params or {}
        header_params.update(self.default_headers)
        if self.cookie:
            header_params['Cookie'] = self.cookie
        if header_params:
            header_params = self.sanitize_for_serialization(header_params)
            header_params = dict(self.parameters_to_tuples(header_params,
                                                           collection_formats))
    
        # path parameters
        if path_params:
            path_params = self.sanitize_for_serialization(path_params)
            path_params = self.parameters_to_tuples(path_params,
                                                    collection_formats)
            for k, v in path_params:
                # specified safe chars, encode everything
                resource_path = resource_path.replace(
                    '{%s}' % k,
                    quote(str(v), safe=config.safe_chars_for_path_param)
                )
    
        # query parameters
        if query_params:
            query_params = self.sanitize_for_serialization(query_params)
            query_params = self.parameters_to_tuples(query_params,
                                                     collection_formats)
    
        # post parameters
        if post_params or files:
            post_params = post_params if post_params else []
            post_params = self.sanitize_for_serialization(post_params)
            post_params = self.parameters_to_tuples(post_params,
                                                    collection_formats)
            post_params.extend(self.files_parameters(files))
    
        # auth setting
        self.update_params_for_auth(header_params, query_params, auth_settings)
    
        # body
        if body:
            body = self.sanitize_for_serialization(body)
    
        # request url
        if _host is None:
            url = self.configuration.host + resource_path
        else:
            # use server/host defined in path or operation instead
            url = _host + resource_path
    
        # perform request and return response
&gt;       response_data = self.request(
            method, url, query_params=query_params, headers=header_params,
            post_params=post_params, body=body,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:180: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3533f110&gt;
method = 'GET'
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceservices/scheduler-configmap-ref-test'
query_params = []
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
post_params = [], body = None, _preload_content = True, _request_timeout = None

    def request(self, method, url, query_params=None, headers=None,
                post_params=None, body=None, _preload_content=True,
                _request_timeout=None):
        """Makes the HTTP request using RESTClient."""
        if method == "GET":
&gt;           return self.rest_client.GET(url,
                                        query_params=query_params,
                                        _preload_content=_preload_content,
                                        _request_timeout=_request_timeout,

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:373: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.rest.RESTClientObject object at 0x7feb3533fc10&gt;
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceservices/scheduler-configmap-ref-test'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
query_params = [], _preload_content = True, _request_timeout = None

    def GET(self, url, headers=None, query_params=None, _preload_content=True,
            _request_timeout=None):
&gt;       return self.request("GET", url,
                            headers=headers,
                            _preload_content=_preload_content,
                            _request_timeout=_request_timeout,
                            query_params=query_params)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/rest.py:244: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.rest.RESTClientObject object at 0x7feb3533fc10&gt;
method = 'GET'
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceservices/scheduler-configmap-ref-test'
query_params = []
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = None, post_params = {}, _preload_content = True, _request_timeout = None

    def request(self, method, url, query_params=None, headers=None,
                body=None, post_params=None, _preload_content=True,
                _request_timeout=None):
        """Perform requests.
    
        :param method: http request method
        :param url: http request url
        :param query_params: query parameters in the url
        :param headers: http request headers
        :param body: request json body, for `application/json`
        :param post_params: request post parameters,
                            `application/x-www-form-urlencoded`
                            and `multipart/form-data`
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        """
        method = method.upper()
        assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT',
                          'PATCH', 'OPTIONS']
    
        if post_params and body:
            raise ApiValueError(
                "body parameter cannot be used with post_params parameter."
            )
    
        post_params = post_params or {}
        headers = headers or {}
    
        timeout = None
        if _request_timeout:
            if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)):  # noqa: E501,F821
                timeout = urllib3.Timeout(total=_request_timeout)
            elif (isinstance(_request_timeout, tuple) and
                  len(_request_timeout) == 2):
                timeout = urllib3.Timeout(
                    connect=_request_timeout[0], read=_request_timeout[1])
    
        if 'Content-Type' not in headers:
            headers['Content-Type'] = 'application/json'
    
        try:
            # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
            if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
                if query_params:
                    url += '?' + urlencode(query_params)
                if (re.search('json', headers['Content-Type'], re.IGNORECASE) or
                        headers['Content-Type'] == 'application/apply-patch+yaml'):
                    if headers['Content-Type'] == 'application/json-patch+json':
                        if not isinstance(body, list):
                            headers['Content-Type'] = \
                                'application/strategic-merge-patch+json'
                    request_body = None
                    if body is not None:
                        request_body = json.dumps(body)
                    r = self.pool_manager.request(
                        method, url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'application/x-www-form-urlencoded':  # noqa: E501
                    r = self.pool_manager.request(
                        method, url,
                        fields=post_params,
                        encode_multipart=False,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'multipart/form-data':
                    # must del headers['Content-Type'], or the correct
                    # Content-Type which generated by urllib3 will be
                    # overwritten.
                    del headers['Content-Type']
                    r = self.pool_manager.request(
                        method, url,
                        fields=post_params,
                        encode_multipart=True,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                # Pass a `string` parameter directly in the body to support
                # other content types than Json when `body` argument is
                # provided in serialized form
                elif isinstance(body, str) or isinstance(body, bytes):
                    request_body = body
                    r = self.pool_manager.request(
                        method, url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                else:
                    # Cannot generate the request from given parameters
                    msg = """Cannot prepare a request message for provided
                             arguments. Please check that your arguments match
                             declared content type."""
                    raise ApiException(status=0, reason=msg)
            # For `GET`, `HEAD`
            else:
                r = self.pool_manager.request(method, url,
                                              fields=query_params,
                                              preload_content=_preload_content,
                                              timeout=timeout,
                                              headers=headers)
        except urllib3.exceptions.SSLError as e:
            msg = "{0}\n{1}".format(type(e).__name__, str(e))
            raise ApiException(status=0, reason=msg)
    
        if _preload_content:
            r = RESTResponse(r)
    
            # In the python 3, the response.data is bytes.
            # we need to decode it to string.
            if six.PY3:
                r.data = r.data.decode('utf8')
    
            # log response body
            logger.debug("response body: %s", r.data)
    
        if not 200 &lt;= r.status &lt;= 299:
&gt;           raise ApiException(http_resp=r)
E           kubernetes.client.exceptions.ApiException: (500)
E           Reason: Internal Server Error
E           HTTP response headers: HTTPHeaderDict({'Audit-Id': '0f8a21c4-7317-45e3-a15e-c82447ad1cc0', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'X-Kubernetes-Pf-Flowschema-Uid': '2a999f06-9419-439a-ad27-f84423daf280', 'X-Kubernetes-Pf-Prioritylevel-Uid': '7646e888-06ea-42cb-b71d-ec0f1b0b2888', 'Date': 'Tue, 14 Jul 2026 16:20:11 GMT', 'Content-Length': '292'})
E           HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"conversion webhook for serving.kserve.io/v1alpha2, Kind=LLMInferenceService failed: Post \"https://llmisvc-webhook-server-service.kserve.svc:443/convert?timeout=30s\": net/http: TLS handshake timeout","code":500}

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/rest.py:238: ApiException

The above exception was the direct cause of the following exception:

test_case = TestCase(base_refs=['router-managed', 'scheduler-with-configmap-ref', 'workload-llmd-simulator'], prompt='KServe is a'...              {'name': 'workload-llmd-simulator-schedul-b2f159da'}]},
 'status': None}, model_name='facebook/opt-125m')

    @pytest.mark.llminferenceservice
    @pytest.mark.asyncio(loop_scope="session")
    @pytest.mark.parametrize(
        "test_case",
        [
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-gateway-ref",
                        "router-with-managed-route",
                        "model-fb-opt-125m",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="custom-route-timeout-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs",
                        "scheduler-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="KServe is a",
                    service_name="router-with-refs-test",
                    expected_gateway=ROUTER_GATEWAYS[0],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[0]],
                            routes=[ROUTER_ROUTES[0], ROUTER_ROUTES[1]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=["router-managed", "workload-pd-cpu", "model-fb-opt-125m"],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-custom-route-timeout-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="custom-route-timeout-pd-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-with-refs-pd",
                        "scheduler-managed",
                        "workload-pd-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="You are an expert in Kubernetes-native machine learning serving platforms, with deep knowledge of the KServe project. "
                    "Explain the challenges of serving large-scale models, GPU scheduling, and how KServe integrates with capabilities like multi-model serving. "
                    "Provide a detailed comparison with open source alternatives, focusing on operational trade-offs.",
                    service_name="router-with-refs-pd-test",
                    response_assertion=assert_200_with_choices,
                    expected_gateway=ROUTER_GATEWAYS[1],
                    before_test=[
                        lambda: create_router_resources(
                            gateways=[ROUTER_GATEWAYS[1]],
                            routes=[ROUTER_ROUTES[2], ROUTER_ROUTES[3]],
                        )
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.custom_gateway,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-dp-ep-gpu",
                        "workload-dp-ep-prefill-gpu",
                        "model-deepseek-v2-lite",
                    ],
                    prompt="Delve into the multifaceted implications of a fully disaggregated cloud architecture, specifically "
                    "where the compute plane (P) and the data plane (D) are independently deployed and managed for a "
                    "geographically distributed, high-throughput, low-latency microservices ecosystem. Beyond the "
                    "fundamental challenges of network latency and data consistency, elaborate on the advanced "
                    "considerations and trade-offs inherent in such a setup: 1. Network Architecture and Protocols: "
                    "How would the network fabric and underlying protocols (e.g., RDMA, custom transport layers) need to "
                    "evolve to support optimal performance and minimize inter-plane communication overhead, especially for "
                    "synchronous operations? Discuss the role of network programmability (e.g., SDN, P4) in dynamically "
                    "optimizing routing and traffic flow between P and D. 2. Advanced Data Consistency and Durability: "
                    "Explore sophisticated data consistency models (e.g., causal consistency, strong eventual consistency) "
                    "and their applicability in balancing performance and data integrity across a globally distributed data plane. "
                    "Detail strategies for ensuring data durability and fault tolerance, including multi-region replication, "
                    "intelligent partitioning, and recovery mechanisms in the event of partial or full plane failures. "
                    "3. Dynamic Resource Orchestration and Cost Optimization: Analyze how an orchestration layer would intelligently "
                    "manage the independent scaling of compute (P) and data (D) resources, considering fluctuating workloads, "
                    "cost efficiency, and performance targets (e.g., using predictive analytics for resource provisioning). "
                    "Discuss mechanisms for dynamically reallocating compute nodes to different data partitions based on "
                    "workload patterns and data locality, potentially involving live migration strategies. "
                    "4. Security and Compliance in a Distributed Landscape: Address the enhanced security perimeter "
                    "challenges, including securing communication channels between P and D (encryption in transit, mutual TLS), "
                    "fine-grained access control to data at rest and in motion, and identity management across disaggregated "
                    "components. Discuss how such an architecture impacts compliance with regulatory frameworks (e.g., GDPR, HIPAA) "
                    "concerning data sovereignty, privacy, and auditability. 5. Operational Complexity and Observability: "
                    "Examine the increased complexity in monitoring, logging, and tracing across highly decoupled compute and "
                    "data planes. What specialized tooling and practices (e.g., distributed tracing with OpenTelemetry, advanced AIOps) "
                    "would be essential? How would incident response and troubleshooting differ in this disaggregated environment "
                    "compared to traditional integrated systems? Consider the challenges of pinpointing root causes across "
                    "independent failures. 6. Real-world Applicability and Future Trends: Identify specific industries "
                    "or use cases (e.g., high-frequency trading, IoT edge processing, large language model inference) "
                    "where the benefits of P/D disaggregation would strongly outweigh its complexities. "
                    "Conclude by speculating on emerging technologies or paradigms (e.g., serverless compute functions "
                    "directly interacting with object storage, in-memory disaggregation) that could further drive or "
                    "transform P/D disaggregation in cloud computing.",
                    max_tokens=2000,
                ),
                marks=[
                    pytest.mark.cluster_gpu,
                    pytest.mark.cluster_nvidia,
                    pytest.mark.cluster_nvidia_roce,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-no-scheduler",
                        "workload-single-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="What is KServe?",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.no_scheduler,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-fb-opt-125m",
                    ],
                    prompt="This test simulates DP+EP that can run on CPU, the idea is to test the LWS-based deployment, "
                    "but without the resources requirements for DP+EP (GPUs and ROCe/IB).",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_multi_node],
            ),
            # Scheduler config tests
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-inline-config",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-inline-config-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Chat completions endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                        "model-qwen2.5-0.5b",
                    ],
                    model_name="Qwen/Qwen2.5-0.5B-Instruct",
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=create_response_assertion(with_field="choices"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-configmap-ref",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-configmap-ref-test",
                    before_test=[create_scheduler_configmap],
                    after_test=[delete_scheduler_configmap],
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-replicas",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-ha-replicas-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-custom-template",
                        "workload-llmd-simulator",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-custom-template-test",
                ),
                marks=[pytest.mark.cluster_cpu, pytest.mark.cluster_single_node],
            ),
            # Scheduler v0.6 → v0.7 migration tests.
            # Deploy v0.6-style configs and verify the controller migrates them
            # so the v0.7 scheduler boots successfully.
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-pd-config-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-pd-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-v06-nonzero-threshold-migration",
                        "workload-llmd-simulator-pd",
                    ],
                    prompt="KServe is a",
                    service_name="scheduler-v06-threshold-migration-test",
                    response_assertion=assert_200_with_choices,
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Precise prefix KV cache routing test
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "scheduler-with-precise-prefix-cache-inline-config",
                        "workload-llmd-simulator-kvcache",
                    ],
                    prompt="KServe is a",
                    service_name="precise-prefix-cache-test",
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Models endpoint coverage
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/models",
                    response_assertion=create_response_assertion(with_field="data"),
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/completions",
                            prompt="KServe is a",
                            payload_formatter=completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/chat/completions
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-llmd-simulator",
                    ],
                    endpoint="/v1/chat/completions",
                    prompt="What is KServe?",
                    payload_formatter=chat_completions_payload,
                    response_assertion=assert_model_field_matches("facebook/opt-125m"),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                    peers=[
                        TestCase(
                            base_refs=[
                                "router-managed",
                                "workload-llmd-simulator",
                                "model-qwen2.5-0.5b",
                            ],
                            endpoint="/v1/chat/completions",
                            prompt="What is KServe?",
                            payload_formatter=chat_completions_payload,
                            response_assertion=assert_model_field_matches(
                                "Qwen/Qwen2.5-0.5B-Instruct"
                            ),
                            url_getter=get_model_routing_url,
                            extra_headers={
                                MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/Qwen/Qwen2.5-0.5B-Instruct",
                            },
                        ),
                    ],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.llmd_simulator,
                    pytest.mark.model_routing,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — LoRA adapter
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/completions",
                    prompt="KServe is a",
                    model_name=f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    payload_formatter=completions_payload,
                    response_assertion=assert_model_field_matches(
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1"
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # Model-based routing via X-Gateway-Model-Name header — /v1/models (base + LoRA)
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-fb-opt-125m-with-lora-hf",
                    ],
                    endpoint="/v1/models",
                    response_assertion=assert_models_contains(
                        "facebook/opt-125m",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                        "lora-adapter-1",
                        f"publishers/{KSERVE_TEST_NAMESPACE}/models/lora-adapter-1",
                    ),
                    url_getter=get_model_routing_url,
                    extra_headers={
                        MODEL_ROUTING_HEADER: f"publishers/{KSERVE_TEST_NAMESPACE}/models/facebook/opt-125m",
                    },
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.model_routing,
                    pytest.mark.lora,
                ],
            ),
            # PVC storage tests -- validate direct PVC volume mount with real vLLM serving
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-single-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-pd-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    response_assertion=assert_200_with_choices,
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_single_node,
                    pytest.mark.pvc_storage,
                ],
            ),
            pytest.param(
                TestCase(
                    base_refs=[
                        "router-managed",
                        "workload-simulated-dp-ep-cpu",
                        "model-pvc",
                    ],
                    prompt="KServe is a",
                    before_test=[ensure_pvc_with_model],
                ),
                marks=[
                    pytest.mark.cluster_cpu,
                    pytest.mark.cluster_multi_node,
                    pytest.mark.pvc_storage,
                ],
            ),
        ],
        indirect=["test_case"],
        ids=generate_test_id,
    )
    @log_execution
    def test_llm_inference_service(test_case: TestCase):  # noqa: F811
        inject_k8s_proxy()
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        service_name = test_case.llm_service.metadata.name
        if not test_case.llm_service.metadata.annotations:
            test_case.llm_service.metadata.annotations = {}
    
        test_case.llm_service.metadata.annotations[
            "security.opendatahub.io/enable-auth"
        ] = "false"
        prefix = test_case.log_prefix
    
        test_failed = False
        try:
            print(f"{prefix} Creating LLMInferenceService {service_name}")
            create_llmisvc(kserve_client, test_case.llm_service)
            print(f"{prefix} Waiting for LLMInferenceService {service_name} to be ready")
&gt;           wait_for_llm_isvc_ready(
                kserve_client, test_case.llm_service, test_case.wait_timeout
            )

llmisvc/test_llm_inference_service.py:807: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (&lt;kserve.api.kserve_client.KServeClient object at 0x7feb353aab90&gt;, {'api_version': 'serving.kserve.io/v1alpha1',
 'kin...ef-sc-67492bcd'},
                       {'name': 'workload-llmd-simulator-schedul-b2f159da'}]},
 'status': None}, 900)
kwargs = {}, func_name = 'wait_for_llm_isvc_ready'
timestamp_start = '2026-07-14T16:14:00.651748', start_time = 1784045640.6520207
duration = 370.385338306427, timestamp_end = '2026-07-14T16:20:11.037362'

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        func_name = func.__name__
    
        timestamp_start = datetime.now().isoformat()
        logger.info(
            f"[{func_name}] [{timestamp_start}] start - args={args}, kwargs={kwargs}"
        )
        start_time = time.time()
    
        try:
&gt;           result = func(*args, **kwargs)

llmisvc/logging.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb353aab90&gt;
given = {'api_version': 'serving.kserve.io/v1alpha1',
 'kind': 'LLMInferenceService',
 'metadata': {'annotations': {'security....gmap-ref-sc-67492bcd'},
                       {'name': 'workload-llmd-simulator-schedul-b2f159da'}]},
 'status': None}
timeout_seconds = 900

    @log_execution
    def wait_for_llm_isvc_ready(
        kserve_client: KServeClient,
        given: V1alpha1LLMInferenceService,
        timeout_seconds: int = 900,
    ) -&gt; str:
        def assert_llm_isvc_ready():
            out = get_llmisvc(
                kserve_client,
                given.metadata.name,
                given.metadata.namespace,
                given.api_version.split("/")[1],
            )
    
            if "status" not in out:
                raise AssertionError("No status found in LLM inference service")
    
            status = out["status"]
            if "conditions" not in status:
                raise AssertionError("No conditions found in status")
    
            expected_true_conditions = {"Ready", "WorkloadsReady", "RouterReady"}
            got_true_conditions = set()
    
            conditions = status["conditions"]
    
            for condition in conditions:
                if condition.get("status") == "True":
                    got_true_conditions.add(condition.get("type"))
    
            missing_conditions = expected_true_conditions - got_true_conditions
            if missing_conditions:
                raise AssertionError(
                    f"Missing true conditions: {missing_conditions}, expected {expected_true_conditions}, got {conditions}"
                )
            return True
    
&gt;       return wait_for(assert_llm_isvc_ready, timeout=timeout_seconds, interval=1.0)

llmisvc/test_llm_inference_service.py:1205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

assertion_fn = &lt;function wait_for_llm_isvc_ready.&lt;locals&gt;.assert_llm_isvc_ready at 0x7feb354da480&gt;
timeout = 900, interval = 1.0

    def wait_for(
        assertion_fn: Callable[[], Any], timeout: float = 5.0, interval: float = 0.1
    ) -&gt; Any:
        """Wait for the assertion to succeed within timeout."""
        deadline = time.time() + timeout
        last_msg = None
        while True:
            try:
&gt;               return assertion_fn()

llmisvc/test_llm_inference_service.py:1216: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def assert_llm_isvc_ready():
&gt;       out = get_llmisvc(
            kserve_client,
            given.metadata.name,
            given.metadata.namespace,
            given.api_version.split("/")[1],
        )

llmisvc/test_llm_inference_service.py:1175: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb353aab90&gt;
name = 'scheduler-configmap-ref-test', namespace = 'kserve-ci-e2e-test'
version = 'v1alpha1'

    def get_llmisvc(
        kserve_client: KServeClient,
        name,
        namespace,
        version=constants.KSERVE_V1ALPHA1_VERSION,
    ):
        try:
            return kserve_client.api_instance.get_namespaced_custom_object(
                constants.KSERVE_GROUP,
                version,
                namespace,
                KSERVE_PLURAL_LLMINFERENCESERVICE,
                name,
            )
        except client.rest.ApiException as e:
&gt;           raise RuntimeError(
                f"❌ Exception when calling CustomObjectsApi-&gt;"
                f"get_namespaced_custom_object for LLMInferenceService: {e}"
            ) from e
E           RuntimeError: ❌ Exception when calling CustomObjectsApi-&gt;get_namespaced_custom_object for LLMInferenceService: (500)
E           Reason: Internal Server Error
E           HTTP response headers: HTTPHeaderDict({'Audit-Id': '0f8a21c4-7317-45e3-a15e-c82447ad1cc0', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'X-Kubernetes-Pf-Flowschema-Uid': '2a999f06-9419-439a-ad27-f84423daf280', 'X-Kubernetes-Pf-Prioritylevel-Uid': '7646e888-06ea-42cb-b71d-ec0f1b0b2888', 'Date': 'Tue, 14 Jul 2026 16:20:11 GMT', 'Content-Length': '292'})
E           HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"conversion webhook for serving.kserve.io/v1alpha2, Kind=LLMInferenceService failed: Post \"https://llmisvc-webhook-server-service.kserve.svc:443/convert?timeout=30s\": net/http: TLS handshake timeout","code":500}

llmisvc/test_llm_inference_service.py:1052: RuntimeError</failure></testcase><testcase classname="llmisvc.test_llm_inference_service" name="test_llm_inference_service[cluster_cpu-cluster_single_node-router-managed-scheduler-with-replicas-workload-llmd-simulator]" time="0.050"><error message="failed on setup with &quot;kubernetes.client.exceptions.ApiException: (500)&#10;Reason: Internal Server Error&#10;HTTP response headers: HTTPHeaderDict({'Audit-Id': 'd74318b4-4b3c-46ed-8d76-4bc09654e289', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'X-Kubernetes-Pf-Flowschema-Uid': '2a999f06-9419-439a-ad27-f84423daf280', 'X-Kubernetes-Pf-Prioritylevel-Uid': '7646e888-06ea-42cb-b71d-ec0f1b0b2888', 'Date': 'Tue, 14 Jul 2026 16:20:13 GMT', 'Content-Length': '701'})&#10;HTTP response body: {&quot;kind&quot;:&quot;Status&quot;,&quot;apiVersion&quot;:&quot;v1&quot;,&quot;metadata&quot;:{},&quot;status&quot;:&quot;Failure&quot;,&quot;message&quot;:&quot;Internal error occurred: failed calling webhook \&quot;llminferenceserviceconfig.kserve-webhook-server.v1alpha1.validator\&quot;: failed to call webhook: Post \&quot;https://llmisvc-webhook-server-service.kserve.svc:443/validate-serving-kserve-io-v1alpha1-llminferenceserviceconfig?timeout=10s\&quot;: EOF&quot;,&quot;reason&quot;:&quot;InternalError&quot;,&quot;details&quot;:{&quot;causes&quot;:[{&quot;message&quot;:&quot;failed calling webhook \&quot;llminferenceserviceconfig.kserve-webhook-server.v1alpha1.validator\&quot;: failed to call webhook: Post \&quot;https://llmisvc-webhook-server-service.kserve.svc:443/validate-serving-kserve-io-v1alpha1-llminferenceserviceconfig?timeout=10s\&quot;: EOF&quot;}]},&quot;code&quot;:500}&quot;">kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb35387810&gt;
llm_config = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
namespace = 'kserve-ci-e2e-test'

    def _create_or_update_llmisvc_config(kserve_client, llm_config, namespace=None):
        """Create or update an LLMInferenceServiceConfig resource."""
        version = llm_config["apiVersion"].split("/")[1]
    
        if namespace is None:
            namespace = llm_config.get("metadata", {}).get("namespace", "default")
    
        name = llm_config.get("metadata", {}).get("name")
        if not name:
            raise ValueError("LLMInferenceServiceConfig must have a name in metadata")
    
        logger.info(f"Checking LLMInferenceServiceConfig {name} in namespace {namespace}")
    
        try:
&gt;           existing_config = kserve_client.api_instance.get_namespaced_custom_object(
                constants.KSERVE_GROUP,
                version,
                namespace,
                KSERVE_PLURAL_LLMINFERENCESERVICECONFIG,
                name,
            )

llmisvc/fixtures.py:1671: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api.custom_objects_api.CustomObjectsApi object at 0x7feb3510c0d0&gt;
group = 'serving.kserve.io', version = 'v1alpha1'
namespace = 'kserve-ci-e2e-test', plural = 'llminferenceserviceconfigs'
name = 'router-managed-scheduler-ha-rep-bf59a7b5'
kwargs = {'_return_http_data_only': True}

    def get_namespaced_custom_object(self, group, version, namespace, plural, name, **kwargs):  # noqa: E501
        """get_namespaced_custom_object  # noqa: E501
    
        Returns a namespace scoped custom object  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        &gt;&gt;&gt; thread = api.get_namespaced_custom_object(group, version, namespace, plural, name, async_req=True)
        &gt;&gt;&gt; result = thread.get()
    
        :param async_req bool: execute request asynchronously
        :param str group: the custom resource's group (required)
        :param str version: the custom resource's version (required)
        :param str namespace: The custom resource's namespace (required)
        :param str plural: the custom resource's plural name. For TPRs this would be lowercase plural kind. (required)
        :param str name: the custom object's name (required)
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return: object
                 If the method is called asynchronously,
                 returns the request thread.
        """
        kwargs['_return_http_data_only'] = True
&gt;       return self.get_namespaced_custom_object_with_http_info(group, version, namespace, plural, name, **kwargs)  # noqa: E501

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api/custom_objects_api.py:1632: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api.custom_objects_api.CustomObjectsApi object at 0x7feb3510c0d0&gt;
group = 'serving.kserve.io', version = 'v1alpha1'
namespace = 'kserve-ci-e2e-test', plural = 'llminferenceserviceconfigs'
name = 'router-managed-scheduler-ha-rep-bf59a7b5'
kwargs = {'_return_http_data_only': True}
local_var_params = {'_return_http_data_only': True, 'all_params': ['group', 'version', 'namespace', 'plural', 'name', 'async_req', ...], 'auth_settings': ['BearerToken'], 'body_params': None, ...}
all_params = ['group', 'version', 'namespace', 'plural', 'name', 'async_req', ...]
key = '_return_http_data_only', val = True, collection_formats = {}
path_params = {'group': 'serving.kserve.io', 'name': 'router-managed-scheduler-ha-rep-bf59a7b5', 'namespace': 'kserve-ci-e2e-test', 'plural': 'llminferenceserviceconfigs', ...}
query_params = []

    def get_namespaced_custom_object_with_http_info(self, group, version, namespace, plural, name, **kwargs):  # noqa: E501
        """get_namespaced_custom_object  # noqa: E501
    
        Returns a namespace scoped custom object  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        &gt;&gt;&gt; thread = api.get_namespaced_custom_object_with_http_info(group, version, namespace, plural, name, async_req=True)
        &gt;&gt;&gt; result = thread.get()
    
        :param async_req bool: execute request asynchronously
        :param str group: the custom resource's group (required)
        :param str version: the custom resource's version (required)
        :param str namespace: The custom resource's namespace (required)
        :param str plural: the custom resource's plural name. For TPRs this would be lowercase plural kind. (required)
        :param str name: the custom object's name (required)
        :param _return_http_data_only: response data without head status code
                                       and headers
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return: tuple(object, status_code(int), headers(HTTPHeaderDict))
                 If the method is called asynchronously,
                 returns the request thread.
        """
    
        local_var_params = locals()
    
        all_params = [
            'group',
            'version',
            'namespace',
            'plural',
            'name'
        ]
        all_params.extend(
            [
                'async_req',
                '_return_http_data_only',
                '_preload_content',
                '_request_timeout'
            ]
        )
    
        for key, val in six.iteritems(local_var_params['kwargs']):
            if key not in all_params:
                raise ApiTypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method get_namespaced_custom_object" % key
                )
            local_var_params[key] = val
        del local_var_params['kwargs']
        # verify the required parameter 'group' is set
        if self.api_client.client_side_validation and ('group' not in local_var_params or  # noqa: E501
                                                        local_var_params['group'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `group` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'version' is set
        if self.api_client.client_side_validation and ('version' not in local_var_params or  # noqa: E501
                                                        local_var_params['version'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `version` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'namespace' is set
        if self.api_client.client_side_validation and ('namespace' not in local_var_params or  # noqa: E501
                                                        local_var_params['namespace'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `namespace` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'plural' is set
        if self.api_client.client_side_validation and ('plural' not in local_var_params or  # noqa: E501
                                                        local_var_params['plural'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `plural` when calling `get_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'name' is set
        if self.api_client.client_side_validation and ('name' not in local_var_params or  # noqa: E501
                                                        local_var_params['name'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `name` when calling `get_namespaced_custom_object`")  # noqa: E501
    
        collection_formats = {}
    
        path_params = {}
        if 'group' in local_var_params:
            path_params['group'] = local_var_params['group']  # noqa: E501
        if 'version' in local_var_params:
            path_params['version'] = local_var_params['version']  # noqa: E501
        if 'namespace' in local_var_params:
            path_params['namespace'] = local_var_params['namespace']  # noqa: E501
        if 'plural' in local_var_params:
            path_params['plural'] = local_var_params['plural']  # noqa: E501
        if 'name' in local_var_params:
            path_params['name'] = local_var_params['name']  # noqa: E501
    
        query_params = []
    
        header_params = {}
    
        form_params = []
        local_var_files = {}
    
        body_params = None
        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.select_header_accept(
            ['application/json'])  # noqa: E501
    
        # Authentication setting
        auth_settings = ['BearerToken']  # noqa: E501
    
&gt;       return self.api_client.call_api(
            '/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}', 'GET',
            path_params,
            query_params,
            header_params,
            body=body_params,
            post_params=form_params,
            files=local_var_files,
            response_type='object',  # noqa: E501
            auth_settings=auth_settings,
            async_req=local_var_params.get('async_req'),
            _return_http_data_only=local_var_params.get('_return_http_data_only'),  # noqa: E501
            _preload_content=local_var_params.get('_preload_content', True),
            _request_timeout=local_var_params.get('_request_timeout'),
            collection_formats=collection_formats)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api/custom_objects_api.py:1739: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3510fa90&gt;
resource_path = '/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}'
method = 'GET'
path_params = {'group': 'serving.kserve.io', 'name': 'router-managed-scheduler-ha-rep-bf59a7b5', 'namespace': 'kserve-ci-e2e-test', 'plural': 'llminferenceserviceconfigs', ...}
query_params = []
header_params = {'Accept': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = None, post_params = [], files = {}, response_type = 'object'
auth_settings = ['BearerToken'], async_req = None, _return_http_data_only = True
collection_formats = {}, _preload_content = True, _request_timeout = None
_host = None

    def call_api(self, resource_path, method,
                 path_params=None, query_params=None, header_params=None,
                 body=None, post_params=None, files=None,
                 response_type=None, auth_settings=None, async_req=None,
                 _return_http_data_only=None, collection_formats=None,
                 _preload_content=True, _request_timeout=None, _host=None):
        """Makes the HTTP request (synchronous) and returns deserialized data.
    
        To make an async_req request, set the async_req parameter.
    
        :param resource_path: Path to method endpoint.
        :param method: Method to call.
        :param path_params: Path parameters in the url.
        :param query_params: Query parameters in the url.
        :param header_params: Header parameters to be
            placed in the request header.
        :param body: Request body.
        :param post_params dict: Request post form parameters,
            for `application/x-www-form-urlencoded`, `multipart/form-data`.
        :param auth_settings list: Auth Settings names for the request.
        :param response: Response data type.
        :param files dict: key -&gt; filename, value -&gt; filepath,
            for `multipart/form-data`.
        :param async_req bool: execute request asynchronously
        :param _return_http_data_only: response data without head status code
                                       and headers
        :param collection_formats: dict of collection formats for path, query,
            header, and post parameters.
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return:
            If async_req parameter is True,
            the request will be called asynchronously.
            The method will return the request thread.
            If parameter async_req is False or missing,
            then the method will return the response directly.
        """
        if not async_req:
&gt;           return self.__call_api(resource_path, method,
                                   path_params, query_params, header_params,
                                   body, post_params, files,
                                   response_type, auth_settings,
                                   _return_http_data_only, collection_formats,
                                   _preload_content, _request_timeout, _host)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:348: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3510fa90&gt;
resource_path = '/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs/router-managed-scheduler-ha-rep-bf59a7b5'
method = 'GET'
path_params = [('group', 'serving.kserve.io'), ('version', 'v1alpha1'), ('namespace', 'kserve-ci-e2e-test'), ('plural', 'llminferenceserviceconfigs'), ('name', 'router-managed-scheduler-ha-rep-bf59a7b5')]
query_params = []
header_params = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = None, post_params = [], files = {}, response_type = 'object'
auth_settings = ['BearerToken'], _return_http_data_only = True
collection_formats = {}, _preload_content = True, _request_timeout = None
_host = None

    def __call_api(
            self, resource_path, method, path_params=None,
            query_params=None, header_params=None, body=None, post_params=None,
            files=None, response_type=None, auth_settings=None,
            _return_http_data_only=None, collection_formats=None,
            _preload_content=True, _request_timeout=None, _host=None):
    
        config = self.configuration
    
        # header parameters
        header_params = header_params or {}
        header_params.update(self.default_headers)
        if self.cookie:
            header_params['Cookie'] = self.cookie
        if header_params:
            header_params = self.sanitize_for_serialization(header_params)
            header_params = dict(self.parameters_to_tuples(header_params,
                                                           collection_formats))
    
        # path parameters
        if path_params:
            path_params = self.sanitize_for_serialization(path_params)
            path_params = self.parameters_to_tuples(path_params,
                                                    collection_formats)
            for k, v in path_params:
                # specified safe chars, encode everything
                resource_path = resource_path.replace(
                    '{%s}' % k,
                    quote(str(v), safe=config.safe_chars_for_path_param)
                )
    
        # query parameters
        if query_params:
            query_params = self.sanitize_for_serialization(query_params)
            query_params = self.parameters_to_tuples(query_params,
                                                     collection_formats)
    
        # post parameters
        if post_params or files:
            post_params = post_params if post_params else []
            post_params = self.sanitize_for_serialization(post_params)
            post_params = self.parameters_to_tuples(post_params,
                                                    collection_formats)
            post_params.extend(self.files_parameters(files))
    
        # auth setting
        self.update_params_for_auth(header_params, query_params, auth_settings)
    
        # body
        if body:
            body = self.sanitize_for_serialization(body)
    
        # request url
        if _host is None:
            url = self.configuration.host + resource_path
        else:
            # use server/host defined in path or operation instead
            url = _host + resource_path
    
        # perform request and return response
&gt;       response_data = self.request(
            method, url, query_params=query_params, headers=header_params,
            post_params=post_params, body=body,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:180: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3510fa90&gt;
method = 'GET'
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs/router-managed-scheduler-ha-rep-bf59a7b5'
query_params = []
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
post_params = [], body = None, _preload_content = True, _request_timeout = None

    def request(self, method, url, query_params=None, headers=None,
                post_params=None, body=None, _preload_content=True,
                _request_timeout=None):
        """Makes the HTTP request using RESTClient."""
        if method == "GET":
&gt;           return self.rest_client.GET(url,
                                        query_params=query_params,
                                        _preload_content=_preload_content,
                                        _request_timeout=_request_timeout,

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:373: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.rest.RESTClientObject object at 0x7feb3510e910&gt;
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs/router-managed-scheduler-ha-rep-bf59a7b5'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
query_params = [], _preload_content = True, _request_timeout = None

    def GET(self, url, headers=None, query_params=None, _preload_content=True,
            _request_timeout=None):
&gt;       return self.request("GET", url,
                            headers=headers,
                            _preload_content=_preload_content,
                            _request_timeout=_request_timeout,
                            query_params=query_params)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/rest.py:244: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.rest.RESTClientObject object at 0x7feb3510e910&gt;
method = 'GET'
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs/router-managed-scheduler-ha-rep-bf59a7b5'
query_params = []
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = None, post_params = {}, _preload_content = True, _request_timeout = None

    def request(self, method, url, query_params=None, headers=None,
                body=None, post_params=None, _preload_content=True,
                _request_timeout=None):
        """Perform requests.
    
        :param method: http request method
        :param url: http request url
        :param query_params: query parameters in the url
        :param headers: http request headers
        :param body: request json body, for `application/json`
        :param post_params: request post parameters,
                            `application/x-www-form-urlencoded`
                            and `multipart/form-data`
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        """
        method = method.upper()
        assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT',
                          'PATCH', 'OPTIONS']
    
        if post_params and body:
            raise ApiValueError(
                "body parameter cannot be used with post_params parameter."
            )
    
        post_params = post_params or {}
        headers = headers or {}
    
        timeout = None
        if _request_timeout:
            if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)):  # noqa: E501,F821
                timeout = urllib3.Timeout(total=_request_timeout)
            elif (isinstance(_request_timeout, tuple) and
                  len(_request_timeout) == 2):
                timeout = urllib3.Timeout(
                    connect=_request_timeout[0], read=_request_timeout[1])
    
        if 'Content-Type' not in headers:
            headers['Content-Type'] = 'application/json'
    
        try:
            # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
            if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
                if query_params:
                    url += '?' + urlencode(query_params)
                if (re.search('json', headers['Content-Type'], re.IGNORECASE) or
                        headers['Content-Type'] == 'application/apply-patch+yaml'):
                    if headers['Content-Type'] == 'application/json-patch+json':
                        if not isinstance(body, list):
                            headers['Content-Type'] = \
                                'application/strategic-merge-patch+json'
                    request_body = None
                    if body is not None:
                        request_body = json.dumps(body)
                    r = self.pool_manager.request(
                        method, url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'application/x-www-form-urlencoded':  # noqa: E501
                    r = self.pool_manager.request(
                        method, url,
                        fields=post_params,
                        encode_multipart=False,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'multipart/form-data':
                    # must del headers['Content-Type'], or the correct
                    # Content-Type which generated by urllib3 will be
                    # overwritten.
                    del headers['Content-Type']
                    r = self.pool_manager.request(
                        method, url,
                        fields=post_params,
                        encode_multipart=True,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                # Pass a `string` parameter directly in the body to support
                # other content types than Json when `body` argument is
                # provided in serialized form
                elif isinstance(body, str) or isinstance(body, bytes):
                    request_body = body
                    r = self.pool_manager.request(
                        method, url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                else:
                    # Cannot generate the request from given parameters
                    msg = """Cannot prepare a request message for provided
                             arguments. Please check that your arguments match
                             declared content type."""
                    raise ApiException(status=0, reason=msg)
            # For `GET`, `HEAD`
            else:
                r = self.pool_manager.request(method, url,
                                              fields=query_params,
                                              preload_content=_preload_content,
                                              timeout=timeout,
                                              headers=headers)
        except urllib3.exceptions.SSLError as e:
            msg = "{0}\n{1}".format(type(e).__name__, str(e))
            raise ApiException(status=0, reason=msg)
    
        if _preload_content:
            r = RESTResponse(r)
    
            # In the python 3, the response.data is bytes.
            # we need to decode it to string.
            if six.PY3:
                r.data = r.data.decode('utf8')
    
            # log response body
            logger.debug("response body: %s", r.data)
    
        if not 200 &lt;= r.status &lt;= 299:
&gt;           raise ApiException(http_resp=r)
E           kubernetes.client.exceptions.ApiException: (404)
E           Reason: Not Found
E           HTTP response headers: HTTPHeaderDict({'Audit-Id': 'e3ab75ba-f660-4d18-84be-736c88a71c31', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'X-Kubernetes-Pf-Flowschema-Uid': '2a999f06-9419-439a-ad27-f84423daf280', 'X-Kubernetes-Pf-Prioritylevel-Uid': '7646e888-06ea-42cb-b71d-ec0f1b0b2888', 'Date': 'Tue, 14 Jul 2026 16:20:13 GMT', 'Content-Length': '338'})
E           HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"llminferenceserviceconfigs.serving.kserve.io \"router-managed-scheduler-ha-rep-bf59a7b5\" not found","reason":"NotFound","details":{"name":"router-managed-scheduler-ha-rep-bf59a7b5","group":"serving.kserve.io","kind":"llminferenceserviceconfigs"},"code":404}

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/rest.py:238: ApiException

During handling of the above exception, another exception occurred:

request = &lt;SubRequest 'ensure_gateway_proxy_memory' for &lt;Function test_llm_inference_service[router-managed-scheduler-with-replicas-workload-llmd-simulator]&gt;&gt;

    @pytest.fixture(autouse=True)
    def ensure_gateway_proxy_memory(request):
        """After test setup creates gateways, patch them for proxy memory."""
        if not GATEWAY_PROXY_MEMORY:
            return
    
        # Establish ordering: if the test uses test_case (llmisvc), let it run first
        if "test_case" in request.fixturenames:
&gt;           request.getfixturevalue("test_case")

common/gateway_proxy_istio.py:159: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = &lt;SubRequest 'test_case' for &lt;Function test_llm_inference_service[router-managed-scheduler-with-replicas-workload-llmd-simulator]&gt;&gt;

    @pytest.fixture(scope="function")
    def test_case(request):
        tc = request.param
    
        inject_k8s_proxy()
    
        kserve_client = KServeClient(
            config_file=os.environ.get("KUBECONFIG", "~/.kube/config"),
            client_configuration=client.Configuration(),
        )
    
        # Execute before test hooks
        try:
            for func in tc.before_test:
                func()
        except Exception as before_test_error:
            raise RuntimeError(
                f"Failed to execute before test hook: {before_test_error}"
            ) from before_test_error
    
        try:
&gt;           _setup_test_case_service(kserve_client, tc, request.node.name)

llmisvc/fixtures.py:1558: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb35387810&gt;
tc = TestCase(base_refs=['router-managed', 'scheduler-with-replicas', 'workload-llmd-simulator'], prompt='KServe is a', ser...None, expected_gateway=None, before_test=[], after_test=[], peers=[], llm_service=None, model_name='facebook/opt-125m')
test_node_name = 'test_llm_inference_service[router-managed-scheduler-with-replicas-workload-llmd-simulator]'
peer_index = None

    def _setup_test_case_service(kserve_client, tc, test_node_name, peer_index=None):
        """Create LLMInferenceServiceConfigs and build the LLMInferenceService for a TestCase.
    
        Returns a list of created config names for cleanup tracking.
        """
        missing_refs = [
            ref for ref in tc.base_refs if ref not in LLMINFERENCESERVICE_CONFIGS
        ]
        if missing_refs:
            raise ValueError(
                f"Missing base_refs in LLMINFERENCESERVICE_CONFIGS: {missing_refs}"
            )
        if not tc.service_name:
            suffix = f"-peer-{peer_index}" if peer_index is not None else ""
            tc.service_name = generate_service_name(test_node_name + suffix, tc.base_refs)
        if tc.model_name == "default/model":
            tc.model_name = _get_model_name_from_configs(tc.base_refs)
    
        created_configs = []
        unique_base_refs = []
        for base_ref in tc.base_refs:
            unique_config_name = generate_k8s_safe_suffix(base_ref, [tc.service_name])
            unique_base_refs.append(unique_config_name)
    
            unique_config_body = {
                "apiVersion": "serving.kserve.io/v1alpha1",
                "kind": "LLMInferenceServiceConfig",
                "metadata": {
                    "name": unique_config_name,
                    "namespace": KSERVE_TEST_NAMESPACE,
                },
                "spec": LLMINFERENCESERVICE_CONFIGS[base_ref],
            }
    
&gt;           _create_or_update_llmisvc_config(
                kserve_client, unique_config_body, KSERVE_TEST_NAMESPACE
            )

llmisvc/fixtures.py:1518: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kserve_client = &lt;kserve.api.kserve_client.KServeClient object at 0x7feb35387810&gt;
llm_config = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
namespace = 'kserve-ci-e2e-test'

    def _create_or_update_llmisvc_config(kserve_client, llm_config, namespace=None):
        """Create or update an LLMInferenceServiceConfig resource."""
        version = llm_config["apiVersion"].split("/")[1]
    
        if namespace is None:
            namespace = llm_config.get("metadata", {}).get("namespace", "default")
    
        name = llm_config.get("metadata", {}).get("name")
        if not name:
            raise ValueError("LLMInferenceServiceConfig must have a name in metadata")
    
        logger.info(f"Checking LLMInferenceServiceConfig {name} in namespace {namespace}")
    
        try:
            existing_config = kserve_client.api_instance.get_namespaced_custom_object(
                constants.KSERVE_GROUP,
                version,
                namespace,
                KSERVE_PLURAL_LLMINFERENCESERVICECONFIG,
                name,
            )
    
            llm_config["metadata"] = existing_config["metadata"]
    
            outputs = kserve_client.api_instance.replace_namespaced_custom_object(
                constants.KSERVE_GROUP,
                version,
                namespace,
                KSERVE_PLURAL_LLMINFERENCESERVICECONFIG,
                name,
                llm_config,
            )
            logger.info(f"✓ Successfully updated LLMInferenceServiceConfig {name}")
            return outputs
    
        except client.rest.ApiException as e:
            if e.status == 404:  # Not found - create it
                logger.info(
                    f"Resource not found, creating LLMInferenceServiceConfig {name}"
                )
&gt;               outputs = kserve_client.api_instance.create_namespaced_custom_object(
                    constants.KSERVE_GROUP,
                    version,
                    namespace,
                    KSERVE_PLURAL_LLMINFERENCESERVICECONFIG,
                    llm_config,
                )

llmisvc/fixtures.py:1697: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api.custom_objects_api.CustomObjectsApi object at 0x7feb3510c0d0&gt;
group = 'serving.kserve.io', version = 'v1alpha1'
namespace = 'kserve-ci-e2e-test', plural = 'llminferenceserviceconfigs'
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
kwargs = {'_return_http_data_only': True}

    def create_namespaced_custom_object(self, group, version, namespace, plural, body, **kwargs):  # noqa: E501
        """create_namespaced_custom_object  # noqa: E501
    
        Creates a namespace scoped Custom object  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        &gt;&gt;&gt; thread = api.create_namespaced_custom_object(group, version, namespace, plural, body, async_req=True)
        &gt;&gt;&gt; result = thread.get()
    
        :param async_req bool: execute request asynchronously
        :param str group: The custom resource's group name (required)
        :param str version: The custom resource's version (required)
        :param str namespace: The custom resource's namespace (required)
        :param str plural: The custom resource's plural name. For TPRs this would be lowercase plural kind. (required)
        :param object body: The JSON schema of the Resource to create. (required)
        :param str pretty: If 'true', then the output is pretty printed.
        :param str dry_run: When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed
        :param str field_manager: fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.
        :param str field_validation: fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. (optional)
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return: object
                 If the method is called asynchronously,
                 returns the request thread.
        """
        kwargs['_return_http_data_only'] = True
&gt;       return self.create_namespaced_custom_object_with_http_info(group, version, namespace, plural, body, **kwargs)  # noqa: E501

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api/custom_objects_api.py:231: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api.custom_objects_api.CustomObjectsApi object at 0x7feb3510c0d0&gt;
group = 'serving.kserve.io', version = 'v1alpha1'
namespace = 'kserve-ci-e2e-test', plural = 'llminferenceserviceconfigs'
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
kwargs = {'_return_http_data_only': True}
local_var_params = {'_return_http_data_only': True, 'all_params': ['group', 'version', 'namespace', 'plural', 'body', 'pretty', ...], 'au...i-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [...]}}}}}, ...}
all_params = ['group', 'version', 'namespace', 'plural', 'body', 'pretty', ...]
key = '_return_http_data_only', val = True, collection_formats = {}
path_params = {'group': 'serving.kserve.io', 'namespace': 'kserve-ci-e2e-test', 'plural': 'llminferenceserviceconfigs', 'version': 'v1alpha1'}
query_params = []

    def create_namespaced_custom_object_with_http_info(self, group, version, namespace, plural, body, **kwargs):  # noqa: E501
        """create_namespaced_custom_object  # noqa: E501
    
        Creates a namespace scoped Custom object  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        &gt;&gt;&gt; thread = api.create_namespaced_custom_object_with_http_info(group, version, namespace, plural, body, async_req=True)
        &gt;&gt;&gt; result = thread.get()
    
        :param async_req bool: execute request asynchronously
        :param str group: The custom resource's group name (required)
        :param str version: The custom resource's version (required)
        :param str namespace: The custom resource's namespace (required)
        :param str plural: The custom resource's plural name. For TPRs this would be lowercase plural kind. (required)
        :param object body: The JSON schema of the Resource to create. (required)
        :param str pretty: If 'true', then the output is pretty printed.
        :param str dry_run: When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed
        :param str field_manager: fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.
        :param str field_validation: fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. (optional)
        :param _return_http_data_only: response data without head status code
                                       and headers
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return: tuple(object, status_code(int), headers(HTTPHeaderDict))
                 If the method is called asynchronously,
                 returns the request thread.
        """
    
        local_var_params = locals()
    
        all_params = [
            'group',
            'version',
            'namespace',
            'plural',
            'body',
            'pretty',
            'dry_run',
            'field_manager',
            'field_validation'
        ]
        all_params.extend(
            [
                'async_req',
                '_return_http_data_only',
                '_preload_content',
                '_request_timeout'
            ]
        )
    
        for key, val in six.iteritems(local_var_params['kwargs']):
            if key not in all_params:
                raise ApiTypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method create_namespaced_custom_object" % key
                )
            local_var_params[key] = val
        del local_var_params['kwargs']
        # verify the required parameter 'group' is set
        if self.api_client.client_side_validation and ('group' not in local_var_params or  # noqa: E501
                                                        local_var_params['group'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `group` when calling `create_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'version' is set
        if self.api_client.client_side_validation and ('version' not in local_var_params or  # noqa: E501
                                                        local_var_params['version'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `version` when calling `create_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'namespace' is set
        if self.api_client.client_side_validation and ('namespace' not in local_var_params or  # noqa: E501
                                                        local_var_params['namespace'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `namespace` when calling `create_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'plural' is set
        if self.api_client.client_side_validation and ('plural' not in local_var_params or  # noqa: E501
                                                        local_var_params['plural'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `plural` when calling `create_namespaced_custom_object`")  # noqa: E501
        # verify the required parameter 'body' is set
        if self.api_client.client_side_validation and ('body' not in local_var_params or  # noqa: E501
                                                        local_var_params['body'] is None):  # noqa: E501
            raise ApiValueError("Missing the required parameter `body` when calling `create_namespaced_custom_object`")  # noqa: E501
    
        collection_formats = {}
    
        path_params = {}
        if 'group' in local_var_params:
            path_params['group'] = local_var_params['group']  # noqa: E501
        if 'version' in local_var_params:
            path_params['version'] = local_var_params['version']  # noqa: E501
        if 'namespace' in local_var_params:
            path_params['namespace'] = local_var_params['namespace']  # noqa: E501
        if 'plural' in local_var_params:
            path_params['plural'] = local_var_params['plural']  # noqa: E501
    
        query_params = []
        if 'pretty' in local_var_params and local_var_params['pretty'] is not None:  # noqa: E501
            query_params.append(('pretty', local_var_params['pretty']))  # noqa: E501
        if 'dry_run' in local_var_params and local_var_params['dry_run'] is not None:  # noqa: E501
            query_params.append(('dryRun', local_var_params['dry_run']))  # noqa: E501
        if 'field_manager' in local_var_params and local_var_params['field_manager'] is not None:  # noqa: E501
            query_params.append(('fieldManager', local_var_params['field_manager']))  # noqa: E501
        if 'field_validation' in local_var_params and local_var_params['field_validation'] is not None:  # noqa: E501
            query_params.append(('fieldValidation', local_var_params['field_validation']))  # noqa: E501
    
        header_params = {}
    
        form_params = []
        local_var_files = {}
    
        body_params = None
        if 'body' in local_var_params:
            body_params = local_var_params['body']
        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.select_header_accept(
            ['application/json'])  # noqa: E501
    
        # Authentication setting
        auth_settings = ['BearerToken']  # noqa: E501
    
&gt;       return self.api_client.call_api(
            '/apis/{group}/{version}/namespaces/{namespace}/{plural}', 'POST',
            path_params,
            query_params,
            header_params,
            body=body_params,
            post_params=form_params,
            files=local_var_files,
            response_type='object',  # noqa: E501
            auth_settings=auth_settings,
            async_req=local_var_params.get('async_req'),
            _return_http_data_only=local_var_params.get('_return_http_data_only'),  # noqa: E501
            _preload_content=local_var_params.get('_preload_content', True),
            _request_timeout=local_var_params.get('_request_timeout'),
            collection_formats=collection_formats)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api/custom_objects_api.py:354: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3510fa90&gt;
resource_path = '/apis/{group}/{version}/namespaces/{namespace}/{plural}'
method = 'POST'
path_params = {'group': 'serving.kserve.io', 'namespace': 'kserve-ci-e2e-test', 'plural': 'llminferenceserviceconfigs', 'version': 'v1alpha1'}
query_params = []
header_params = {'Accept': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
post_params = [], files = {}, response_type = 'object'
auth_settings = ['BearerToken'], async_req = None, _return_http_data_only = True
collection_formats = {}, _preload_content = True, _request_timeout = None
_host = None

    def call_api(self, resource_path, method,
                 path_params=None, query_params=None, header_params=None,
                 body=None, post_params=None, files=None,
                 response_type=None, auth_settings=None, async_req=None,
                 _return_http_data_only=None, collection_formats=None,
                 _preload_content=True, _request_timeout=None, _host=None):
        """Makes the HTTP request (synchronous) and returns deserialized data.
    
        To make an async_req request, set the async_req parameter.
    
        :param resource_path: Path to method endpoint.
        :param method: Method to call.
        :param path_params: Path parameters in the url.
        :param query_params: Query parameters in the url.
        :param header_params: Header parameters to be
            placed in the request header.
        :param body: Request body.
        :param post_params dict: Request post form parameters,
            for `application/x-www-form-urlencoded`, `multipart/form-data`.
        :param auth_settings list: Auth Settings names for the request.
        :param response: Response data type.
        :param files dict: key -&gt; filename, value -&gt; filepath,
            for `multipart/form-data`.
        :param async_req bool: execute request asynchronously
        :param _return_http_data_only: response data without head status code
                                       and headers
        :param collection_formats: dict of collection formats for path, query,
            header, and post parameters.
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        :return:
            If async_req parameter is True,
            the request will be called asynchronously.
            The method will return the request thread.
            If parameter async_req is False or missing,
            then the method will return the response directly.
        """
        if not async_req:
&gt;           return self.__call_api(resource_path, method,
                                   path_params, query_params, header_params,
                                   body, post_params, files,
                                   response_type, auth_settings,
                                   _return_http_data_only, collection_formats,
                                   _preload_content, _request_timeout, _host)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:348: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3510fa90&gt;
resource_path = '/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs'
method = 'POST'
path_params = [('group', 'serving.kserve.io'), ('version', 'v1alpha1'), ('namespace', 'kserve-ci-e2e-test'), ('plural', 'llminferenceserviceconfigs')]
query_params = []
header_params = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
post_params = [], files = {}, response_type = 'object'
auth_settings = ['BearerToken'], _return_http_data_only = True
collection_formats = {}, _preload_content = True, _request_timeout = None
_host = None

    def __call_api(
            self, resource_path, method, path_params=None,
            query_params=None, header_params=None, body=None, post_params=None,
            files=None, response_type=None, auth_settings=None,
            _return_http_data_only=None, collection_formats=None,
            _preload_content=True, _request_timeout=None, _host=None):
    
        config = self.configuration
    
        # header parameters
        header_params = header_params or {}
        header_params.update(self.default_headers)
        if self.cookie:
            header_params['Cookie'] = self.cookie
        if header_params:
            header_params = self.sanitize_for_serialization(header_params)
            header_params = dict(self.parameters_to_tuples(header_params,
                                                           collection_formats))
    
        # path parameters
        if path_params:
            path_params = self.sanitize_for_serialization(path_params)
            path_params = self.parameters_to_tuples(path_params,
                                                    collection_formats)
            for k, v in path_params:
                # specified safe chars, encode everything
                resource_path = resource_path.replace(
                    '{%s}' % k,
                    quote(str(v), safe=config.safe_chars_for_path_param)
                )
    
        # query parameters
        if query_params:
            query_params = self.sanitize_for_serialization(query_params)
            query_params = self.parameters_to_tuples(query_params,
                                                     collection_formats)
    
        # post parameters
        if post_params or files:
            post_params = post_params if post_params else []
            post_params = self.sanitize_for_serialization(post_params)
            post_params = self.parameters_to_tuples(post_params,
                                                    collection_formats)
            post_params.extend(self.files_parameters(files))
    
        # auth setting
        self.update_params_for_auth(header_params, query_params, auth_settings)
    
        # body
        if body:
            body = self.sanitize_for_serialization(body)
    
        # request url
        if _host is None:
            url = self.configuration.host + resource_path
        else:
            # use server/host defined in path or operation instead
            url = _host + resource_path
    
        # perform request and return response
&gt;       response_data = self.request(
            method, url, query_params=query_params, headers=header_params,
            post_params=post_params, body=body,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:180: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.api_client.ApiClient object at 0x7feb3510fa90&gt;
method = 'POST'
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs'
query_params = []
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
post_params = []
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
_preload_content = True, _request_timeout = None

    def request(self, method, url, query_params=None, headers=None,
                post_params=None, body=None, _preload_content=True,
                _request_timeout=None):
        """Makes the HTTP request using RESTClient."""
        if method == "GET":
            return self.rest_client.GET(url,
                                        query_params=query_params,
                                        _preload_content=_preload_content,
                                        _request_timeout=_request_timeout,
                                        headers=headers)
        elif method == "HEAD":
            return self.rest_client.HEAD(url,
                                         query_params=query_params,
                                         _preload_content=_preload_content,
                                         _request_timeout=_request_timeout,
                                         headers=headers)
        elif method == "OPTIONS":
            return self.rest_client.OPTIONS(url,
                                            query_params=query_params,
                                            headers=headers,
                                            _preload_content=_preload_content,
                                            _request_timeout=_request_timeout)
        elif method == "POST":
&gt;           return self.rest_client.POST(url,
                                         query_params=query_params,
                                         headers=headers,
                                         post_params=post_params,
                                         _preload_content=_preload_content,
                                         _request_timeout=_request_timeout,

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/api_client.py:391: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.rest.RESTClientObject object at 0x7feb3510e910&gt;
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
query_params = [], post_params = []
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
_preload_content = True, _request_timeout = None

    def POST(self, url, headers=None, query_params=None, post_params=None,
             body=None, _preload_content=True, _request_timeout=None):
&gt;       return self.request("POST", url,
                            headers=headers,
                            query_params=query_params,
                            post_params=post_params,
                            _preload_content=_preload_content,
                            _request_timeout=_request_timeout,
                            body=body)

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/rest.py:279: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;kubernetes.client.rest.RESTClientObject object at 0x7feb3510e910&gt;
method = 'POST'
url = 'https://a135768385b8548aeb30cc8b7f8c4c78-4e67f7c4ac32c090.elb.us-east-1.amazonaws.com:6443/apis/serving.kserve.io/v1alpha1/namespaces/kserve-ci-e2e-test/llminferenceserviceconfigs'
query_params = []
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'OpenAPI-Generator/32.0.1/python'}
body = {'apiVersion': 'serving.kserve.io/v1alpha1', 'kind': 'LLMInferenceServiceConfig', 'metadata': {'name': 'router-managed...ve-ci-e2e-test'}, 'spec': {'router': {'gateway': {}, 'route': {}, 'scheduler': {'template': {'containers': [{...}]}}}}}
post_params = {}, _preload_content = True, _request_timeout = None

    def request(self, method, url, query_params=None, headers=None,
                body=None, post_params=None, _preload_content=True,
                _request_timeout=None):
        """Perform requests.
    
        :param method: http request method
        :param url: http request url
        :param query_params: query parameters in the url
        :param headers: http request headers
        :param body: request json body, for `application/json`
        :param post_params: request post parameters,
                            `application/x-www-form-urlencoded`
                            and `multipart/form-data`
        :param _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        """
        method = method.upper()
        assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT',
                          'PATCH', 'OPTIONS']
    
        if post_params and body:
            raise ApiValueError(
                "body parameter cannot be used with post_params parameter."
            )
    
        post_params = post_params or {}
        headers = headers or {}
    
        timeout = None
        if _request_timeout:
            if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)):  # noqa: E501,F821
                timeout = urllib3.Timeout(total=_request_timeout)
            elif (isinstance(_request_timeout, tuple) and
                  len(_request_timeout) == 2):
                timeout = urllib3.Timeout(
                    connect=_request_timeout[0], read=_request_timeout[1])
    
        if 'Content-Type' not in headers:
            headers['Content-Type'] = 'application/json'
    
        try:
            # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
            if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
                if query_params:
                    url += '?' + urlencode(query_params)
                if (re.search('json', headers['Content-Type'], re.IGNORECASE) or
                        headers['Content-Type'] == 'application/apply-patch+yaml'):
                    if headers['Content-Type'] == 'application/json-patch+json':
                        if not isinstance(body, list):
                            headers['Content-Type'] = \
                                'application/strategic-merge-patch+json'
                    request_body = None
                    if body is not None:
                        request_body = json.dumps(body)
                    r = self.pool_manager.request(
                        method, url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'application/x-www-form-urlencoded':  # noqa: E501
                    r = self.pool_manager.request(
                        method, url,
                        fields=post_params,
                        encode_multipart=False,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'multipart/form-data':
                    # must del headers['Content-Type'], or the correct
                    # Content-Type which generated by urllib3 will be
                    # overwritten.
                    del headers['Content-Type']
                    r = self.pool_manager.request(
                        method, url,
                        fields=post_params,
                        encode_multipart=True,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                # Pass a `string` parameter directly in the body to support
                # other content types than Json when `body` argument is
                # provided in serialized form
                elif isinstance(body, str) or isinstance(body, bytes):
                    request_body = body
                    r = self.pool_manager.request(
                        method, url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                else:
                    # Cannot generate the request from given parameters
                    msg = """Cannot prepare a request message for provided
                             arguments. Please check that your arguments match
                             declared content type."""
                    raise ApiException(status=0, reason=msg)
            # For `GET`, `HEAD`
            else:
                r = self.pool_manager.request(method, url,
                                              fields=query_params,
                                              preload_content=_preload_content,
                                              timeout=timeout,
                                              headers=headers)
        except urllib3.exceptions.SSLError as e:
            msg = "{0}\n{1}".format(type(e).__name__, str(e))
            raise ApiException(status=0, reason=msg)
    
        if _preload_content:
            r = RESTResponse(r)
    
            # In the python 3, the response.data is bytes.
            # we need to decode it to string.
            if six.PY3:
                r.data = r.data.decode('utf8')
    
            # log response body
            logger.debug("response body: %s", r.data)
    
        if not 200 &lt;= r.status &lt;= 299:
&gt;           raise ApiException(http_resp=r)
E           kubernetes.client.exceptions.ApiException: (500)
E           Reason: Internal Server Error
E           HTTP response headers: HTTPHeaderDict({'Audit-Id': 'd74318b4-4b3c-46ed-8d76-4bc09654e289', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'X-Kubernetes-Pf-Flowschema-Uid': '2a999f06-9419-439a-ad27-f84423daf280', 'X-Kubernetes-Pf-Prioritylevel-Uid': '7646e888-06ea-42cb-b71d-ec0f1b0b2888', 'Date': 'Tue, 14 Jul 2026 16:20:13 GMT', 'Content-Length': '701'})
E           HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Internal error occurred: failed calling webhook \"llminferenceserviceconfig.kserve-webhook-server.v1alpha1.validator\": failed to call webhook: Post \"https://llmisvc-webhook-server-service.kserve.svc:443/validate-serving-kserve-io-v1alpha1-llminferenceserviceconfig?timeout=10s\": EOF","reason":"InternalError","details":{"causes":[{"message":"failed calling webhook \"llminferenceserviceconfig.kserve-webhook-server.v1alpha1.validator\": failed to call webhook: Post \"https://llmisvc-webhook-server-service.kserve.svc:443/validate-serving-kserve-io-v1alpha1-llminferenceserviceconfig?timeout=10s\": EOF"}]},"code":500}

../../python/kserve/.venv/lib64/python3.11/site-packages/kubernetes/client/rest.py:238: ApiException</error></testcase></testsuite></testsuites>