docstr: This function helps to deploy a frozen VM on each ESXi host of the resource pool
  specified in the frozen VM config under the vSphere config section. So that we
  can spread the Ray nodes on different ESXi host at the beginning.
code: def create_frozen_vm_on_each_host(
      self, node_config, name, resource_pool_name
  ):
      """
      This function helps to deploy a frozen VM on each ESXi host of the resource pool
      specified in the frozen VM config under the vSphere config section. So that we
      can spread the Ray nodes on different ESXi host at the beginning.
      """
      exception_happened = False
      vm_names = []
      cluster_id = self.get_pyvmomi_sdk_provider().get_cluster_id_of_resource_pool(
          resource_pool_name
      )
      hosts = self.get_vsphere_sdk_provider().list_all_hosts_in_cluster(
          cluster_id
      )
  
      futures_frozen_vms = []
      with ThreadPoolExecutor(max_workers=len(hosts)) as executor:
          for host in hosts:
              node_config_frozen_vm = copy.deepcopy(node_config)
              node_config_frozen_vm["host_id"] = host.host
  
              frozen_vm_name = "{}-{}-{}".format(
                  name, host.name, now_ts()
              )
              vm_names.append(frozen_vm_name)
  
              futures_frozen_vms.append(
                  executor.submit(
                      self.create_frozen_vm_from_ovf,
                      node_config_frozen_vm,
                      frozen_vm_name,
                  )
              )
  
      for future in futures_frozen_vms:
          try:
              future.result()
          except Exception as e:
              logger.error(
                  "Exception occurred while creating frozen VMs {}".format(
                      e
                  )
              )
              exception_happened = True
  
      # We clean up all the created VMs if any exception occurs.
      if exception_happened:
          with ThreadPoolExecutor(max_workers=len(hosts)) as executor:
              futures = [
                  executor.submit(
                      self.get_vsphere_sdk_provider().delete_vm_by_name,
                      vm_names[i],
                  )
                  for i in range(len(futures_frozen_vms))
              ]
          for future in futures:
              _ = future.result()
          raise RuntimeError("Failed creating frozen VMs, exiting!")
  
imports: import copy
  import logging
  import threading
  import uuid
  from concurrent.futures import ThreadPoolExecutor
  from typing import Any, Dict
  from pyVmomi import vim
  from ray.autoscaler._private.vsphere.config import bootstrap_vsphere, is_dynamic_passthrough
  from ray.autoscaler._private.vsphere.gpu_utils import get_gpu_cards_from_vm, get_vm_2_gpu_cards_map, plug_gpu_cards_to_vm, set_gpu_placeholder, split_vm_2_gpu_cards_map
  from ray.autoscaler._private.vsphere.pyvmomi_sdk_provider import PyvmomiSdkProvider
  from ray.autoscaler._private.vsphere.scheduler import SchedulerFactory
  from ray.autoscaler._private.vsphere.utils import Constants, now_ts
  from ray.autoscaler._private.vsphere.vsphere_sdk_provider import VsphereSdkProvider
  from ray.autoscaler.node_provider import NodeProvider
  from ray.autoscaler.tags import TAG_RAY_CLUSTER_NAME, TAG_RAY_NODE_NAME
label_desc: Use image node_id 15 for calling the VsphereNodeProvider obj's underlying member method code with example usage: obj.create_frozen_vm_on_each_host(node_config, name, resource_pool_name) without return types