From f6224a533f9b0ac939e676e155cc7c2e4d4d4eaa Mon Sep 17 00:00:00 2001 From: German <28149841+germa89@users.noreply.github.com> Date: Fri, 3 May 2024 12:26:17 +0200 Subject: [PATCH] Allowing passing IP to MapdlPool (#3048) * Allowing passing IP to MapdlPool * add docs * Fix vale * Using ports --------- Co-authored-by: Camille <78221213+clatapie@users.noreply.github.com> --- doc/source/user_guide/pool.rst | 7 ++++ src/ansys/mapdl/core/launcher.py | 2 +- src/ansys/mapdl/core/pool.py | 63 +++++++++++++++++++++++++++++++- tests/test_pool.py | 22 +++++++++++ 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/doc/source/user_guide/pool.rst b/doc/source/user_guide/pool.rst index 8b03c701a9..2d95338a21 100644 --- a/doc/source/user_guide/pool.rst +++ b/doc/source/user_guide/pool.rst @@ -41,6 +41,13 @@ their ports when creating the pool. 'MAPDL Pool with 5 active instances' >>> pool.exit(block=True) +You can also specify a list of IP addresses to connect to: + +.. code:: pycon + + >>> pool = MapdlPool(ip=["127.0.0.2", "127.0.0.3", "127.0.0.4"]) + Creating Pool: 100%|########| 3/3 [00:01<00:00, 1.43it/s] + You can access each individual MAPDL instance with this code: .. code:: pycon diff --git a/src/ansys/mapdl/core/launcher.py b/src/ansys/mapdl/core/launcher.py index d5761ecf02..1ab800dc25 100644 --- a/src/ansys/mapdl/core/launcher.py +++ b/src/ansys/mapdl/core/launcher.py @@ -1170,7 +1170,7 @@ def launch_mapdl( override the default behavior of this keyword argument with the environment variable ``PYMAPDL_START_INSTANCE=FALSE``. - ip : bool, optional + ip : str, optional Used only when ``start_instance`` is ``False``. If provided, and ``start_instance`` (or its correspondent environment variable ``PYMAPDL_START_INSTANCE``) is ``True`` then, an exception is raised. diff --git a/src/ansys/mapdl/core/pool.py b/src/ansys/mapdl/core/pool.py index 18c2791119..3bf6d65612 100755 --- a/src/ansys/mapdl/core/pool.py +++ b/src/ansys/mapdl/core/pool.py @@ -23,6 +23,7 @@ """This module is for threaded implementations of the mapdl interface""" import os import shutil +import socket import tempfile import time from typing import Any, Dict, List, Optional, Union @@ -32,7 +33,9 @@ from ansys.mapdl.core import LOG, launch_mapdl from ansys.mapdl.core.errors import MapdlRuntimeError, VersionError from ansys.mapdl.core.launcher import ( + LOCALHOST, MAPDL_DEFAULT_PORT, + check_valid_ip, get_start_instance, port_in_use, ) @@ -173,11 +176,15 @@ def __init__( override=True, start_instance: bool = None, exec_file: Optional[str] = None, + ip: Optional[str] = None, **kwargs, ) -> None: """Initialize several instances of mapdl""" self._instances: List[None] = [] + # Getting debug arguments + _debug_no_launch = kwargs.pop("_debug_no_launch", None) + if run_location is None: run_location = tempfile.gettempdir() self._root_dir: str = run_location @@ -189,8 +196,40 @@ def __init__( self._exiting_i: int = 0 self._override = override - # Getting start_instance - start_instance = get_start_instance(start_instance) + # Getting IP from env var + ip_env_var = os.environ.get("PYMAPDL_IP", "") + if ip_env_var != "": + if ip: + warnings.warn( + "The env var 'PYMAPDL_IP' is set, hence the 'ip' argument is overwritten." + ) + + ip = ip_env_var + LOG.debug(f"An IP ({ip}) has been set using 'PYMAPDL_IP' env var.") + + ip = None if ip == "" else ip # Making sure the variable is not empty + + if ip is None: + ips = LOCALHOST + + else: + if not isinstance(ip, (tuple, list)): + ips = [ip] + else: + ips = ip + + # Converting ip or hostname to ip + ips = [socket.gethostbyname(each) for each in ips] + _ = [check_valid_ip(each) for each in ips] # double check + + # Getting "start_instance" using "True" as default. + if (ip is not None) and (start_instance is None): + # An IP has been supplied. By default, 'start_instance' is equal + # false, unless it is set through the env vars. + start_instance = get_start_instance(start_instance=False) + else: + start_instance = get_start_instance(start_instance=start_instance) + self._start_instance = start_instance LOG.debug(f"'start_instance' equals to '{start_instance}'") @@ -257,6 +296,15 @@ def __init__( raise ValueError( "The number of instances should be the same as the number of ports." ) + + if ips == LOCALHOST: + ips = [LOCALHOST for each in ports] + + if len(ports) != len(ips): + raise ValueError( + "The number of ips should be the same as the number of ports." + ) + LOG.debug(f"Using ports: {ports}") self._instances = [] @@ -280,6 +328,17 @@ def __init__( self._instances = [None for _ in range(n_instances)] # threaded spawn + if _debug_no_launch: + self._debug_no_launch = { + "ports": ports, + "ips": ips, + "names": self._names, + "start_instance": start_instance, + "exec_file": exec_file, + "n_instances": n_instances, + } + return + threads = [ self._spawn_mapdl( i, diff --git a/tests/test_pool.py b/tests/test_pool.py index f1374a7bb3..c2e906e528 100644 --- a/tests/test_pool.py +++ b/tests/test_pool.py @@ -337,6 +337,28 @@ def test_only_one_instance(): pool.exit() +def test_ip(monkeypatch): + monkeypatch.delenv("PYMAPDL_START_INSTANCE", raising=False) + monkeypatch.delenv("PYMAPDL_IP", raising=False) + + ips = ["127.0.0.1", "127.0.0.2", "127.0.0.3"] + ports = [50083, 50100, 50898] + pool_ = MapdlPool( + 3, + ip=ips, + port=ports, + exec_file=EXEC_FILE, + nproc=NPROC, + additional_switches=QUICK_LAUNCH_SWITCHES, + _debug_no_launch=True, + ) + args = pool_._debug_no_launch + + assert not args["start_instance"] # Because of ip + assert args["ips"] == ips + assert args["ports"] == ports + + def test_next(pool): # Check the instances are free for each_instance in pool: