docstr: Tests local node provider config validation for the most common use
  case of bootstrapping a cluster at a static set of ips.
docstr_tok: ["Tests","local","node","provider","config","validation","for","the","most","common","use","case","of","bootstrapping","a","cluster","at","a","static","set","of","ips","."]
code: def testValidateLocal(self):
      """
      Tests local node provider config validation for the most common use
      case of bootstrapping a cluster at a static set of ips.
      """
      local_config_path = os.path.join(
          RAY_PATH, "autoscaler/local/example-minimal-manual.yaml"
      )
      base_config = yaml.safe_load(open(local_config_path).read())
      base_config["provider"]["head_ip"] = "xxx.yyy"
      base_config["provider"]["worker_ips"] = [
          "aaa.bbb",
          "ccc.ddd",
          "eee.fff",
      ]
      base_config["auth"]["ssh_user"] = "user"
      base_config["auth"]["ssh_private_key"] = "~/.ssh/id_rsa"
  
      test_prepare_config = copy.deepcopy(base_config)
      prepared_config = prepare_config(test_prepare_config)
      try:
          validate_config(prepared_config)
      except Exception:
          self.fail(
              "Failed to validate local/example-minimal-manual.yaml"
          )
      expected_prepared = yaml.safe_load(EXPECTED_LOCAL_CONFIG_STR)
      assert prepared_config == expected_prepared
  
      no_worker_config = copy.deepcopy(base_config)
      del no_worker_config["provider"]["worker_ips"]
      with pytest.raises(ClickException):
          prepare_config(no_worker_config)
      no_head_config = copy.deepcopy(base_config)
      del no_head_config["provider"]["head_ip"]
      with pytest.raises(ClickException):
          prepare_config(no_head_config)
      for field in "head_node", "worker_nodes", "available_node_types":
          faulty_config = copy.deepcopy(base_config)
          faulty_config[field] = "This field shouldn't be in here."
          with pytest.raises(ClickException):
              prepare_config(faulty_config)
  
      too_many_workers_config = copy.deepcopy(base_config)
  
      # More workers requested than the three available ips.
      too_many_workers_config["max_workers"] = 10
      too_many_workers_config["min_workers"] = 10
      prepared_config = prepare_config(too_many_workers_config)
  
      # Check that worker config numbers were clipped to 3.
      assert prepared_config == expected_prepared
  
      not_enough_workers_config = copy.deepcopy(base_config)
  
      # Max workers is less than than the three available ips.
      # The user is probably has probably made an error. Make sure we log a warning.
      not_enough_workers_config["max_workers"] = 0
      not_enough_workers_config["min_workers"] = 0
      with mock.patch(
          "ray.autoscaler._private.local.config.cli_logger.warning"
      ) as warning:
          prepared_config = prepare_config(not_enough_workers_config)
          warning.assert_called_with(
              "The value of `max_workers` supplied (0) is less"
              " than the number of available worker ips (3)."
              " At most 0 Ray worker nodes will connect to the cluster."
          )
      expected_prepared = yaml.safe_load(EXPECTED_LOCAL_CONFIG_STR)
      # We logged a warning.
      # However, prepare_config does not repair the strange config setting:
      expected_prepared["max_workers"] = 0
      expected_prepared["available_node_types"]["local.cluster.node"][
          "max_workers"
      ] = 0
      expected_prepared["available_node_types"]["local.cluster.node"][
          "min_workers"
      ] = 0
      assert prepared_config == expected_prepared
  
imports: import copy
  import logging
  import os
  import sys
  import tempfile
  from typing import Dict, Any
  import unittest
  import urllib
  from unittest.mock import MagicMock, Mock, patch
  import jsonschema
  import pytest
  import yaml
  from click.exceptions import ClickException
  import mock
  from ray._private.test_utils import load_test_config, recursive_fnmatch
  from ray.autoscaler._private._azure.config import _configure_key_pair
  from ray.autoscaler._private.gcp import config
  from ray.autoscaler._private.providers import _NODE_PROVIDERS
  from ray.autoscaler._private.util import fill_node_type_min_max_workers, merge_setup_commands, prepare_config, validate_config
label_desc: Use image node_id 4 for calling the AutoscalingConfigTest obj's underlying member method code with example usage: obj.testValidateLocal() without return types