Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow pool to connect to remote MAPDL instances #2862

Merged
merged 27 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9ae3dd3
Allowing to connect to remote instances in pool library
germa89 Mar 4, 2024
2a5407b
Better error logging
germa89 Mar 6, 2024
d574d71
checking port len
germa89 Mar 7, 2024
54fe72e
Update tests
germa89 Mar 7, 2024
7a0521a
Fix local
germa89 Mar 8, 2024
6f17ff6
Merge branch 'main' into feat/allow-pool-to-connect-to-remote-instances
germa89 Mar 8, 2024
36ffa04
Fixing launcher
germa89 Mar 8, 2024
d3a69e2
Merge branch 'main' into feat/allow-pool-to-connect-to-remote-instances
germa89 Mar 8, 2024
5735cbd
Run tests on remote and local
germa89 Mar 9, 2024
00f55fe
setting ports in remote.
germa89 Mar 9, 2024
3679d26
Launching two docker images
germa89 Mar 9, 2024
a4fcf5d
fix cicd runs
germa89 Mar 9, 2024
25c84bd
fixing port issue
germa89 Mar 9, 2024
4e84f87
skipping one test if not in local
germa89 Mar 9, 2024
ea4138f
Reusing port for re-spawning
germa89 Mar 9, 2024
115d065
Using full verbose
germa89 Mar 9, 2024
23be131
reducing the length of testing
germa89 Mar 9, 2024
0993219
Using different instance names
germa89 Mar 11, 2024
248a0eb
Changing log file names
germa89 Mar 11, 2024
5aa15ec
Skipping if ON_STUDENT
germa89 Mar 11, 2024
2be7f9e
fixing logs
germa89 Mar 11, 2024
bb31b09
fix doc build
germa89 Mar 11, 2024
ac1faad
Uploading docker launch log
germa89 Mar 11, 2024
ba93a87
fixing extracting logs
germa89 Mar 11, 2024
a76e89b
Update src/ansys/mapdl/core/launcher.py
germa89 Mar 11, 2024
b390589
Fixing codacy suggestions
germa89 Mar 11, 2024
fdb89c3
Merge branch 'main' into feat/allow-pool-to-connect-to-remote-instances
germa89 Mar 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .ci/collect_mapdl_logs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ mkdir "$LOG_NAMES" && echo "Successfully generated directory $LOG_NAMES"
####
echo "Collecting MAPDL logs..."

docker exec "$MAPDL_INSTANCE" /bin/bash -c "mkdir -p /mapdl_logs && echo 'Successfully created directory inside docker container'" || echo "Failed to create a directory inside docker container for logs."
docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$FILE*.out' > /dev/null ;then cp -f /file*.out /mapdl_logs && echo 'Successfully copied out files.'; fi" || echo "Failed to copy the 'out' files into a local file"
docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$FILE*.err' > /dev/null ;then cp -f /file*.err /mapdl_logs && echo 'Successfully copied err files.'; fi" || echo "Failed to copy the 'err' files into a local file"
docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$FILE*.log' > /dev/null ;then cp -f /file*.log /mapdl_logs && echo 'Successfully copied log files.'; fi" || echo "Failed to copy the 'log' files into a local file"
docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$WDIR*.crash' > /dev/null ;then cp -f /*.crash /mapdl_logs && echo 'Successfully copied crash files.'; fi" || echo "Failed to copy the 'crash' files into a local file"
(docker exec "$MAPDL_INSTANCE" /bin/bash -c "mkdir -p /mapdl_logs && echo 'Successfully created directory inside docker container'") || echo "Failed to create a directory inside docker container for logs."
(docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$FILE*.out' > /dev/null ;then cp -f /file*.out /mapdl_logs && echo 'Successfully copied out files.'; fi") || echo "Failed to copy the 'out' files into a local file"
(docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$FILE*.err' > /dev/null ;then cp -f /file*.err /mapdl_logs && echo 'Successfully copied err files.'; fi") || echo "Failed to copy the 'err' files into a local file"
(docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$FILE*.log' > /dev/null ;then cp -f /file*.log /mapdl_logs && echo 'Successfully copied log files.'; fi") || echo "Failed to copy the 'log' files into a local file"
(docker exec "$MAPDL_INSTANCE" /bin/bash -c "if compgen -G '$WDIR*.crash' > /dev/null ;then cp -f /*.crash /mapdl_logs && echo 'Successfully copied crash files.'; fi") || echo "Failed to copy the 'crash' files into a local file"

docker cp "$MAPDL_INSTANCE":/mapdl_logs/. ./"$LOG_NAMES"/. || echo "Failed to copy the 'log-build-docs' files into a local directory"

Expand Down
3 changes: 2 additions & 1 deletion .ci/start_mapdl.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/bash
echo "MAPDL Instance name: $INSTANCE_NAME"
echo "MAPDL_VERSION: $MAPDL_VERSION"

export MAPDL_IMAGE="$MAPDL_PACKAGE:$MAPDL_VERSION"
Expand Down Expand Up @@ -28,7 +29,7 @@ echo "P_SCHEMA: $P_SCHEMA"

docker run \
--entrypoint "/bin/bash" \
--name mapdl \
--name "$INSTANCE_NAME" \
--restart always \
--health-cmd="ps aux | grep \"[/]ansys_inc/.*ansys\.e.*grpc\" -q && echo 0 || echo 1" \
--health-interval=0.5s \
Expand Down
27 changes: 19 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ env:
MEILISEARCH_PUBLIC_API_KEY: ${{ secrets.MEILISEARCH_PUBLIC_API_KEY }}
PYANSYS_OFF_SCREEN: True
DPF_START_SERVER: False
DPF_PORT: 21002
DPF_PORT: 21004
MAPDL_PACKAGE: ghcr.io/ansys/mapdl
MAPDL_IMAGE_VERSION_DOCS_BUILD: v24.1-ubuntu-student
ON_CI: True
PYTEST_ARGUMENTS: '-vv --durations=10 --maxfail=3 --reruns 3 --reruns-delay 4 --cov=ansys.mapdl.core --cov-report=html'
PYTEST_ARGUMENTS: '-vvv --durations=10 --maxfail=3 --reruns 3 --reruns-delay 4 --cov=ansys.mapdl.core --cov-report=html'

# Following env vars when changed will "reset" the mentioned cache,
# by changing the cache file name. It is rendered as ...-v%RESET_XXX%-...
Expand Down Expand Up @@ -131,6 +131,7 @@ jobs:
MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }}
DISTRIBUTED_MODE: "dmp"
run: |
export INSTANCE_NAME=MAPDL_0
.ci/start_mapdl.sh &> mapdl_launch.log & export DOCKER_PID=$!
echo "Launching MAPDL service at PID: $DOCKER_PID"
echo "DOCKER_PID=$(echo $DOCKER_PID)" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -261,7 +262,7 @@ jobs:
if: always()
env:
MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }}
MAPDL_INSTANCE: mapdl
MAPDL_INSTANCE: MAPDL_0
LOG_NAMES: logs-build-docs
run: |
.ci/collect_mapdl_logs.sh
Expand All @@ -281,7 +282,6 @@ jobs:
run: |
.ci/display_logs.sh


build-test:
name: "Remote: Build & test MAPDL ${{ matrix.mapdl-version }} (Extended testing ${{ matrix.extended_testing }})"
runs-on: ubuntu-latest
Expand All @@ -308,7 +308,9 @@ jobs:
mapdl-version: 'v24.1.0'
env:
PYMAPDL_PORT: 21000 # default won't work on GitHub runners
PYMAPDL_DB_PORT: 21001 # default won't work on GitHub runners
PYMAPDL_PORT2: 21001 # for the pool testing and default won't work on GitHub runners
PYMAPDL_DB_PORT: 21002 # default won't work on GitHub runners
PYMAPDL_DB_PORT2: 21003 # default won't work on GitHub runners
PYMAPDL_START_INSTANCE: FALSE
ON_LOCAL: FALSE
ON_UBUNTU: FALSE
Expand Down Expand Up @@ -345,9 +347,18 @@ jobs:
MAPDL_VERSION: ${{ matrix.mapdl-version }}
DISTRIBUTED_MODE: ${{ steps.distributed_mode.outputs.distributed_mode }}
run: |
.ci/start_mapdl.sh &> mapdl_launch.log & export DOCKER_PID=$!
echo "Launching MAPDL service at PID: $DOCKER_PID"
echo "DOCKER_PID=$(echo $DOCKER_PID)" >> $GITHUB_OUTPUT
echo "Launching first MAPDL instance..."
export INSTANCE_NAME=MAPDL_0
.ci/start_mapdl.sh &> mapdl_launch_0.log & export DOCKER_PID_0=$!
echo "Launching a second instance for MAPDL pool testing..."
export PYMAPDL_PORT=${{ env.PYMAPDL_PORT2 }}
export PYMAPDL_DB_PORT=${{ env.PYMAPDL_DB_PORT2 }}
export INSTANCE_NAME=MAPDL_1
.ci/start_mapdl.sh &> mapdl_launch_1.log & export DOCKER_PID_1=$!
echo "Launching MAPDL service 0 at PID: $DOCKER_PID_0"
echo "Launching MAPDL service 1 at PID: $DOCKER_PID_2"
echo "DOCKER_PID_0=$(echo $DOCKER_PID_0)" >> $GITHUB_OUTPUT
echo "DOCKER_PID_1=$(echo $DOCKER_PID_1)" >> $GITHUB_OUTPUT

- name: "DPF server activation"
run: |
Expand Down
4 changes: 3 additions & 1 deletion src/ansys/mapdl/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,9 @@ def wrapper(*args, **kwargs):

# Must close unfinished processes
mapdl._close_process()
raise MapdlExitedError("MAPDL server connection terminated") from None
raise MapdlExitedError(
f"MAPDL server connection terminated with the following error\n{error}"
) from None

if threading.current_thread().__class__.__name__ == "_MainThread":
received_interrupt = bool(SIGINT_TRACKER)
Expand Down
5 changes: 5 additions & 0 deletions src/ansys/mapdl/core/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,11 @@ def get_start_instance(start_instance: bool = True):
)
return start_instance

elif start_instance is None:
LOG.debug(
"'PYMAPDL_START_INSTANCE' is unset, and there is no supplied value. Using default, which is 'True'."
)
return True # Default is true
else:
raise ValueError("Only booleans are allowed as arguments.")

Expand Down
5 changes: 4 additions & 1 deletion src/ansys/mapdl/core/mapdl_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,9 @@ def _run(self, cmd: str, verbose: bool = False, mute: Optional[bool] = None) ->
mute = self._mute

if self._exited:
raise MapdlExitedError
raise MapdlExitedError(
f"The MAPDL instance has been exited before running the command: {cmd}"
)

# don't allow empty commands
if not cmd.strip():
Expand Down Expand Up @@ -1141,6 +1143,7 @@ def _close_process(self, timeout=2): # pragma: no cover
processes making this method ineffective for a local instance of MAPDL.

"""
self._log.debug("Closing processes")
if self._local:
# killing server process
self._kill_server()
Expand Down
125 changes: 88 additions & 37 deletions src/ansys/mapdl/core/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@
import shutil
import tempfile
import time
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Union
import warnings

from ansys.mapdl.core import LOG, launch_mapdl
from ansys.mapdl.core.errors import MapdlRuntimeError, VersionError
from ansys.mapdl.core.launcher import MAPDL_DEFAULT_PORT, port_in_use
from ansys.mapdl.core.launcher import (
MAPDL_DEFAULT_PORT,
get_start_instance,
port_in_use,
)
from ansys.mapdl.core.mapdl_grpc import _HAS_TQDM
from ansys.mapdl.core.misc import create_temp_dir, threaded, threaded_daemon

Expand Down Expand Up @@ -114,9 +118,15 @@ class LocalMapdlPool:
the index of each instance in the pool.
By default, the instances directories are named as "Instances_{i}".

start_instance : bool, optional
Set it to ``False`` to make PyMAPDL to connect to remote instances instead
of launching them. In that case, you need to supply the MAPDL instances
ports as a list of ``int`` s.

**kwargs : dict, optional
Additional keyword arguments. For a complete listing, see the description for the
:func:`ansys.mapdl.core.launcher.launch_mapdl` method.
Additional keyword arguments. For a complete listing, see the
description for the :func:`ansys.mapdl.core.launcher.launch_mapdl`
method.

Examples
--------
Expand Down Expand Up @@ -154,12 +164,14 @@ def __init__(
n_instances: int,
wait: bool = True,
run_location: Optional[str] = None,
port: int = MAPDL_DEFAULT_PORT,
port: Union[int, List[int]] = MAPDL_DEFAULT_PORT,
progress_bar: bool = DEFAULT_PROGRESS_BAR,
restart_failed: bool = True,
remove_temp_files: bool = True,
names: Optional[str] = None,
override=True,
start_instance: bool = None,
exec_file: Optional[str] = None,
**kwargs,
) -> None:
"""Initialize several instances of mapdl"""
Expand All @@ -176,6 +188,11 @@ def __init__(
self._exiting_i: int = 0
self._override = override

# Getting start_instance
start_instance = get_start_instance(start_instance)
self._start_instance = start_instance
LOG.debug(f"'start_instance' equals to '{start_instance}'")

if not names:
names = "Instance"

Expand All @@ -188,35 +205,56 @@ def __init__(
"Only strings or functions are allowed in the argument 'name'."
)

# verify that mapdl is 2021R1 or newer
if "exec_file" in kwargs:
exec_file = kwargs["exec_file"]
else: # get default executable
if _HAS_ATP:
exec_file = get_ansys_path()
else:
raise ValueError(
"Please use 'exec_file' argument to specify the location of the ansys installation.\n"
"Alternatively, PyMAPDL can detect your ansys installation if you install 'ansys-tools-path' library."
)
# verify executable
exec_file = os.getenv("PYMAPDL_MAPDL_EXEC", exec_file)

if exec_file is None:
raise FileNotFoundError(
"Invalid exec_file path or cannot load cached "
"ansys path. Enter one manually using "
"exec_file=<path to executable>"
)
if start_instance:
exec_file = kwargs.get("exec_file", exec_file)

if not exec_file: # get default executable
if _HAS_ATP:
exec_file = get_ansys_path()
else:
raise ValueError(
"Please use 'exec_file' argument to specify the location of the ansys installation.\n"
"Alternatively, PyMAPDL can detect your ansys installation if you install 'ansys-tools-path' library."
)

if exec_file is None:
raise FileNotFoundError(
"Invalid exec_file path or cannot load cached "
"ansys path. Enter one manually using "
"exec_file=<path to executable>"
)

if _HAS_ATP:
if version_from_path("mapdl", exec_file) < 211:
raise VersionError("LocalMapdlPool requires MAPDL 2021R1 or later.")
# Checking version
if _HAS_ATP:
if version_from_path("mapdl", exec_file) < 211:
raise VersionError("LocalMapdlPool requires MAPDL 2021R1 or later.")

self._exec_file = exec_file

# grab available ports
ports = available_ports(n_instances, port)
if start_instance:
if isinstance(port, int) or len(port) == 1:
ports = available_ports(n_instances, port)
else:
ports = port

if self._root_dir is not None:
if not os.path.isdir(self._root_dir):
os.makedirs(self._root_dir)
if self._root_dir is not None:
if not os.path.isdir(self._root_dir):
os.makedirs(self._root_dir)
else:
if isinstance(port, int) or len(port) == 1:
ports = [port + i for i in range(n_instances)]
else:
ports = port

if len(ports) != n_instances:
raise ValueError(
"The number of instances should be the same as the number of ports."
)
LOG.debug(f"Using ports: {ports}")

self._instances = []
self._active = True # used by pool monitor
Expand All @@ -241,7 +279,13 @@ def __init__(
# threaded spawn
threads = [
self._spawn_mapdl(
i, ports[i], pbar, name=self._names(i), thread_name=self._names(i)
i,
ports[i],
pbar,
name=self._names(i),
thread_name=self._names(i),
start_instance=start_instance,
exec_file=exec_file,
)
for i in range(n_instances)
]
Expand Down Expand Up @@ -612,8 +656,6 @@ def next_available(self, return_index=False):
return instance, i
else:
return instance
else:
instance._exited = True

def __del__(self):
self.exit()
Expand Down Expand Up @@ -688,7 +730,13 @@ def __iter__(self):

@threaded_daemon
def _spawn_mapdl(
self, index: int, port: int = None, pbar: Optional[bool] = None, name: str = ""
self,
index: int,
port: int = None,
pbar: Optional[bool] = None,
name: str = "",
start_instance=True,
exec_file=None,
):
"""Spawn a mapdl instance at an index"""
# create a new temporary directory for each instance
Expand All @@ -697,9 +745,11 @@ def _spawn_mapdl(
run_location = create_temp_dir(self._root_dir, name=name)

self._instances[index] = launch_mapdl(
exec_file=exec_file,
run_location=run_location,
port=port,
override=True,
start_instance=start_instance,
**self._spawn_kwargs,
)

Expand All @@ -726,20 +776,21 @@ def _monitor_pool(self, refresh=1.0):
"""
while self._active:
for index, instance in enumerate(self._instances):
name = self._names[index]
name = self._names(index)
if not instance: # encountered placeholder
continue

if instance._exited:
try:
# use the next port after the current available port
self._spawning_i += 1
port = max(self._ports) + 1

self._spawn_mapdl(
index,
port=port,
port=instance.port,
name=name,
thread_name=name,
exec_file=self._exec_file,
start_instance=self._start_instance,
).join()

except Exception as e:
Expand Down
Loading
Loading