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

feat: add PyHPS CLI #3091

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
85d371e
First iteration
germa89 May 13, 2024
1da9ca4
Adding logging
germa89 May 14, 2024
4f32315
fixes to the CLI help
germa89 May 14, 2024
08d2055
Fix codacy issues
germa89 May 14, 2024
08731de
Merge branch 'main' into feat/pyhps-support
germa89 May 14, 2024
d70b090
Apply suggestions from code review
germa89 May 16, 2024
aa8a23c
Apply suggestions from code review
germa89 May 16, 2024
660e717
Adding suggestions
germa89 May 16, 2024
33ac15a
Removing warnings.
germa89 May 17, 2024
4055c60
Adding inputs and outputs (#3112)
germa89 May 22, 2024
f23da00
Adding APDL jobs support (#3111)
germa89 May 22, 2024
0f80a47
Refactoring PyHPS implementation (#3117)
germa89 May 31, 2024
a7adf0b
Merge branch 'main' into feat/pyhps-support
germa89 Jun 5, 2024
dff728d
Apply suggestions from Kathy's code review
germa89 Jun 18, 2024
a9eb0e8
Merge branch 'main' into feat/pyhps-support
germa89 Jun 18, 2024
445949a
Adding changelog entry: 3091.miscellaneous.md
pyansys-ci-bot Jun 18, 2024
4556c49
Adding changelog entry: 3091.miscellaneous.md
pyansys-ci-bot Jun 18, 2024
ac875f1
Renaming argument ``to_json``.
germa89 Jun 18, 2024
1911729
rewriting docstring
germa89 Jun 18, 2024
9bc7612
Merge branch 'main' into feat/pyhps-support
germa89 Jun 24, 2024
0279ffc
Adding changelog entry: 3091.added.md
pyansys-ci-bot Jun 24, 2024
dd0cb4b
Merge branch 'main' into feat/pyhps-support
germa89 Jun 26, 2024
4145688
feat: Detaching logging from main logic (#3205)
germa89 Jun 26, 2024
6807934
fix: codecov suggestions
germa89 Jun 26, 2024
1b53047
feat: renaming PyMAPDLJobSubmissionDefinition class
germa89 Jul 15, 2024
a29589d
docs: improved docstring
germa89 Jul 16, 2024
b9a6c98
feat: renaming to submission.
germa89 Jul 16, 2024
e1b85b8
fix: doc example
germa89 Jul 16, 2024
e0bd844
docs:improve docstring examples
germa89 Jul 16, 2024
6cfa2eb
feat: rename file to match main function 'submit'
germa89 Jul 16, 2024
3b55c89
feat: adding option to pass token to CLI.
germa89 Jul 16, 2024
ef24249
docs: adding API docs
germa89 Jul 17, 2024
82e3209
chore: Merge branch 'main' into feat/pyhps-support
germa89 Jul 17, 2024
7af9298
docs: using other type of reference
germa89 Jul 17, 2024
a78aa4c
feat: adding imports to hpc.__init__
germa89 Jul 17, 2024
79615f8
Merge branch 'main' into feat/pyhps-support
germa89 Jan 21, 2025
72c309c
ci: auto fixes from pre-commit.com hooks.
pre-commit-ci[bot] Jan 21, 2025
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
1 change: 1 addition & 0 deletions doc/changelog.d/3091.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat: add PyHPS CLI
1 change: 1 addition & 0 deletions doc/changelog.d/3205.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat: Detaching logging from main logic
25 changes: 25 additions & 0 deletions doc/source/api/hpc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.. _ref_hpc_api:

.. currentmodule:: ansys.mapdl.core.hpc.pyhps

HPC Submissions
===============

.. autosummary::
:toctree: _autosummary

SubmissionDefinition



.. currentmodule:: ansys.mapdl.core.hpc.login

HPC login
=========

.. autosummary::
:toctree: _autosummary

get_token_access


1 change: 1 addition & 0 deletions doc/source/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ PyMAPDL, see :ref:`ref_mapdl_commands`.
database
geometry
helper
hpc
information
inline
krylov
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ doc = [
"vtk==9.3.1",
]

hps =[
"ansys-hps-client==0.8.0",
"keyring==25.2.1",
]

[tool.flit.module]
name = "ansys.mapdl.core"

Expand Down
30 changes: 30 additions & 0 deletions src/ansys/mapdl/core/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@

from ansys.mapdl.core import _HAS_CLICK

try:
from ansys.hps.client import Client

_HAS_HPS = True
except ModuleNotFoundError:
_HAS_HPS = False

if _HAS_CLICK:
###################################
# PyMAPDL CLI
Expand All @@ -42,6 +49,29 @@ def main(ctx: click.Context):
main.add_command(stop)
main.add_command(list_instances, name="list")

# HPC commands
# pymapdl (hpc) login
# pymapdl (hpc) submit
# pymapdl (hpc) list #To be implemented
# pymapdl (hpc) stop #To be implemented

if _HAS_HPS:
from ansys.mapdl.core.cli.hpc import submit
from ansys.mapdl.core.cli.login import login, logout

main.add_command(login)
main.add_command(submit)
main.add_command(logout)

def old_pymapdl_convert_script_entry_point():
print(
"""This CLI function has been deprecated. Use the following instead:

pymapdl convert input_file.inp -o output_file.out ...

For more information, see `PyMAPDL command line interface <https://mapdl.docs.pyansys.com/version/dev/user_guide/cli.html>`_.
"""
)

else:

Expand Down
3 changes: 3 additions & 0 deletions src/ansys/mapdl/core/cli/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ def convert(
)

else:
if not filename_in:
raise ValueError("A file path must be provided.")

convert_script(
filename_in,
filename_out,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/mapdl/core/cli/list_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
@main.command(
short_help="List MAPDL running instances.",
help="""This command list MAPDL instances""",
name="list",
)
@click.option(
"--instances",
Expand Down
284 changes: 284 additions & 0 deletions src/ansys/mapdl/core/cli/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
# Copyright (C) 2016 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Login into a PyHPS cluster"""
from getpass import getpass
import logging
from typing import Optional

import click

logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger()


@click.command(
name="login",
short_help="Login into an HPS cluster.",
help="""Login into an HPS cluster.

It does store credentials (cluster url, password and username) in the OS credential manager.
If you want to change any credential, just issue the command again with the new values.

Examples
--------

Prompt the values for user, password and HPC cluster URL:

$ pymapdl login
Username: myuser
Password: mypassword
HPS cluster URL: https://123.456.789.1:3000/hps
Stored credentials:
User : 'user'
Cluster URL : 'https://123.456.789.1:3000/hps'

Use the CLI arguments to supply the values

$ pymapdl login --user myuser --password mypassword --url "https://123.456.789.1:3000/hps"
Stored credentials:
User : 'user'
Cluster URL : 'https://123.456.789.1:3000/hps'

Set the defaults user, password and URL. They will be used when one of them is
missing.

$ pymapdl login --default --user myuser --password mypassword --url "https://123.456.789.1:3000/hps"
Stored default credentials.

It is possible to input some arguments using the CLI arguments, and other using
the prompt:

$ pymapdl login --user myuser --url "https://123.456.789.1:3000/hps"
Password: mypassword
Stored credentials:
User : 'user'
Cluster URL : 'https://123.456.789.1:3000/hps'

""",
)
@click.option("--user", default=None, type=str, help="The username to login.")
@click.option("--password", default=None, type=str, help="The password to login.")
@click.option(
"--url",
default=None,
type=str,
help="The HPS cluster URL. For instance 'https://123.456.789.1:3000/hps'.",
)
@click.option(
"--default",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Set the default user, password and URL. These credentials are not tested against any HPC.""",
)
@click.option(
"--test_token",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Test if the token is valid. This argument is ignored if '--default' argument is ``True``.""",
)
@click.option(
"--quiet",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Suppress all console printout.""",
)
@click.option(
"--debug",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Activate debugging printout. It might show the input password!""",
)
def login(
user: Optional[str] = None,
password: Optional[str] = None,
url: Optional[str] = None,
default: bool = False,
test_token: bool = False,
quiet: bool = False,
debug: bool = False,
):
from ansys.mapdl.core.hpc.login import login_in_cluster, store_credentials

if debug:
logger.setLevel(logging.DEBUG)

if quiet:
import urllib3

urllib3.disable_warnings()

logger.debug("Storing non-default credentials.")
if not user:
user = click.prompt("Username")

if not user:
raise ValueError("No user was provided.")

if not password:
password = getpass("Password: ")
if not password:
raise ValueError("No password was provided.")

if not default and not url:
url = click.prompt("HPS cluster URL")
if not url:
raise ValueError("No password was provided.")

token = login_in_cluster(user, password, url)
logger.debug("Login successful")

if test_token:
logger.debug("Testing token")
from requests import ConnectionError

from ansys.mapdl.core.hpc.login import token_is_valid

if not token_is_valid(url, token):
raise ConnectionError("The retrieved token is not valid.")
else:
if not quiet:
click.echo("Token has been verified with the HPC cluster.")

logger.info(f"Stored credentials: {user}, {password}, {url}")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.

Copilot Autofix AI about 1 month ago

To fix the problem, we need to ensure that sensitive information such as passwords is not logged. Instead of logging the password, we can log a placeholder or omit it entirely from the log message. This change should be made on line 172 where the sensitive information is being logged.

The best way to fix this without changing existing functionality is to modify the log message to exclude the password. We can log the user and URL while omitting the password. This change will be made in the login function in the file src/ansys/mapdl/core/cli/login.py.

Suggested changeset 1
src/ansys/mapdl/core/cli/login.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/ansys/mapdl/core/cli/login.py b/src/ansys/mapdl/core/cli/login.py
--- a/src/ansys/mapdl/core/cli/login.py
+++ b/src/ansys/mapdl/core/cli/login.py
@@ -171,3 +171,3 @@
 
-    logger.info(f"Stored credentials: {user}, {password}, {url}")
+    logger.info(f"Stored credentials: {user}, [REDACTED], {url}")
     store_credentials(user, password, url, default=default)
EOF
@@ -171,3 +171,3 @@

logger.info(f"Stored credentials: {user}, {password}, {url}")
logger.info(f"Stored credentials: {user}, [REDACTED], {url}")
store_credentials(user, password, url, default=default)
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
store_credentials(user, password, url, default=default)

if not quiet:
if default:
click.echo("Stored default credentials.")
else:
click.echo(
f"Stored credentials:\n User : '{user}'\n Cluster URL : '{url}'"
)


@click.command(
short_help="Logout from an HPS cluster.",
help="""Logout from an HPS cluster.

It deletes credentials stored on the system.

Examples
--------

Delete the credentials associated to an specific URL

$ pymapdl logout --url "https://123.456.789.1:3000/hps"
The HPS cluster 'https://123.456.789.1:3000/hps' credentials have been deleted.

Delete the default credentials.

$ pymapdl logout --default
The default credentials have been deleted.

Notes
-----
- If the credentials do not exist, the CLI notifies and exits cleanly.
No exception is raised. If you want to raise an exception (exit 1), then pass
the argument ``--strict``.
""",
)
@click.option(
"--url",
default=None,
type=str,
help="The HPS cluster URL. For instance 'https://10.231.106.1:3000/hps'.",
)
@click.option(
"--default",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Deletes the default login configuration.""",
)
@click.option(
"--quiet",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Suppress all console printout.""",
)
@click.option(
"--debug",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Activate debugging printout.""",
)
@click.option(
"--strict",
default=False,
type=bool,
is_flag=False,
flag_value=True,
help="""Raise an issue if the credentials do not exist.""",
)
def logout(url, default, quiet, debug, strict):

# TODO: keyrings library seems to not being able to list the credentials
# under a service name. We might need to keep track of those in a file or
# something.

import keyring

from ansys.mapdl.core.hpc.login import delete_credentials

if debug:
logger.setLevel(logging.DEBUG)

if not url and not default:
raise ValueError("An URL needs to be used.")

if url and default:
raise ValueError("The argument '--default' cannot be used with an URL.")

if default:
logger.debug("Deleting credentials for the default profile.")
url = None

try:
delete_credentials(url)
success_message = "The {0} credentials have been deleted.".format(
"default" if default else f"HPS cluster '{url}'"
)
except keyring.errors.PasswordDeleteError:
success_message = "The {0} credentials do not exist.".format(
"default" if default else f"HPS cluster '{url}'"
)
if strict:
raise keyring.errors.PasswordDeleteError(success_message)

if not quiet:
click.echo(success_message)
Loading
Loading