diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d46fb2a24..12bb9d24d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -67,8 +67,12 @@ /python/requirements_ml_docker.txt @amogkam @sven1977 @richardliaw @matthewdeng # Ray symbol export -src/ray/ray_version_script.lds @mwtian @iycheng @ericl @scv119 -src/ray/ray_exported_symbols.lds @mwtian @iycheng @ericl @scv119 +/src/ray/ray_version_script.lds @mwtian @iycheng @ericl @scv119 +/src/ray/ray_exported_symbols.lds @mwtian @iycheng @ericl @scv119 + +# Ray usage stats +/python/ray/_private/usage/ @ericl @richardliaw @rkooo567 @jjyao +/dashboard/modules/usage_stats/ @ericl @richardliaw @rkooo567 @jjyao # ==== Build and CI ==== diff --git a/python/ray/_private/usage/usage_constants.py b/python/ray/_private/usage/usage_constants.py index 06e1e072c..77d7db421 100644 --- a/python/ray/_private/usage/usage_constants.py +++ b/python/ray/_private/usage/usage_constants.py @@ -5,3 +5,8 @@ CLUSTER_METADATA_KEY = b"CLUSTER_METADATA" # The name of a json file where usage stats will be written. USAGE_STATS_FILE = "usage_stats.json" + +USAGE_STATS_HEADS_UP_MESSAGE = ( + "Usage stats collection will be enabled by default in the next release. " + "See https://github.com/ray-project/ray/issues/20857 for more details." +) diff --git a/python/ray/_private/usage/usage_lib.py b/python/ray/_private/usage/usage_lib.py index 3a3af8348..c7415b256 100644 --- a/python/ray/_private/usage/usage_lib.py +++ b/python/ray/_private/usage/usage_lib.py @@ -11,7 +11,7 @@ https://docs.google.com/document/d/1ZT-l9YbGHh-iWRUC91jS-ssQ5Qe2UQ43Lsoc1edCalc/ The module consists of 2 parts. ## Public API -It contains public APIs to obtain usage report information. +It contains public APIs to obtain usage report information. APIs will be added before the usage report becomes opt-in by default. ## Internal APIs for usage processing/report @@ -120,6 +120,10 @@ def _usage_stats_enabled(): return int(os.getenv("RAY_USAGE_STATS_ENABLED", "0")) == 1 +def _usage_stats_prompt_enabled(): + return int(os.getenv("RAY_USAGE_STATS_PROMPT_ENABLED", "1")) == 1 + + def _generate_cluster_metadata(): """Return a dictionary of cluster metadata.""" ray_version, python_version = ray._private.utils.compute_version_info() @@ -144,6 +148,17 @@ def _generate_cluster_metadata(): return metadata +def print_usage_stats_heads_up_message() -> None: + try: + if (not _usage_stats_prompt_enabled()) or _usage_stats_enabled(): + return + + print(usage_constant.USAGE_STATS_HEADS_UP_MESSAGE, file=sys.stderr) + except Exception: + # Silently ignore the exception since it doesn't affect the use of ray. + pass + + def put_cluster_metadata(gcs_client, num_retries) -> None: """Generate the cluster metadata and store it to GCS. diff --git a/python/ray/scripts/scripts.py b/python/ray/scripts/scripts.py index bf9d51d22..7c589e9f0 100644 --- a/python/ray/scripts/scripts.py +++ b/python/ray/scripts/scripts.py @@ -15,6 +15,7 @@ import yaml import ray import psutil +from ray._private.usage import usage_lib import ray._private.services as services import ray.ray_constants as ray_constants import ray._private.utils @@ -610,6 +611,8 @@ def start( if head: # Start head node. + usage_lib.print_usage_stats_heads_up_message() + if port is None: port = ray_constants.DEFAULT_PORT @@ -1126,6 +1129,8 @@ def up( use_login_shells, ): """Create or update a Ray cluster.""" + usage_lib.print_usage_stats_heads_up_message() + if restart_only or no_restart: cli_logger.doassert( restart_only != no_restart, @@ -1446,6 +1451,8 @@ def submit( cli_logger.newline() if start: + usage_lib.print_usage_stats_heads_up_message() + create_or_update_cluster( config_file=cluster_config_file, override_min_workers=None, @@ -1552,6 +1559,9 @@ def exec( """Execute a command via SSH on a Ray cluster.""" port_forward = [(port, port) for port in list(port_forward)] + if start: + usage_lib.print_usage_stats_heads_up_message() + exec_cluster( cluster_config_file, cmd=cmd, diff --git a/python/ray/tests/test_cli_patterns/test_ray_start.txt b/python/ray/tests/test_cli_patterns/test_ray_start.txt index 628520e4f..32deeedfe 100644 --- a/python/ray/tests/test_cli_patterns/test_ray_start.txt +++ b/python/ray/tests/test_cli_patterns/test_ray_start.txt @@ -1,3 +1,4 @@ +Usage stats collection will be enabled by default in the next release\. See https://github.com/ray-project/ray/issues/20857 for more details\. Local node IP: .+ -------------------- diff --git a/python/ray/tests/test_cli_patterns/test_ray_up.txt b/python/ray/tests/test_cli_patterns/test_ray_up.txt index 2b0a881f0..33e68454a 100644 --- a/python/ray/tests/test_cli_patterns/test_ray_up.txt +++ b/python/ray/tests/test_cli_patterns/test_ray_up.txt @@ -1,3 +1,4 @@ +Usage stats collection will be enabled by default in the next release\. See https://github.com/ray-project/ray/issues/20857 for more details\. Cluster: test-cli Checking AWS environment settings diff --git a/python/ray/tests/test_cli_patterns/test_ray_up_docker.txt b/python/ray/tests/test_cli_patterns/test_ray_up_docker.txt index 2b0a881f0..33e68454a 100644 --- a/python/ray/tests/test_cli_patterns/test_ray_up_docker.txt +++ b/python/ray/tests/test_cli_patterns/test_ray_up_docker.txt @@ -1,3 +1,4 @@ +Usage stats collection will be enabled by default in the next release\. See https://github.com/ray-project/ray/issues/20857 for more details\. Cluster: test-cli Checking AWS environment settings diff --git a/python/ray/tests/test_cli_patterns/test_ray_up_record.txt b/python/ray/tests/test_cli_patterns/test_ray_up_record.txt index 10e879037..e878b18e0 100644 --- a/python/ray/tests/test_cli_patterns/test_ray_up_record.txt +++ b/python/ray/tests/test_cli_patterns/test_ray_up_record.txt @@ -1,3 +1,4 @@ +Usage stats collection will be enabled by default in the next release\. See https://github.com/ray-project/ray/issues/20857 for more details\. .+\.py.*Cluster: test-cli .+\.py.*Checking AWS environment settings .+\.py.*Creating new IAM instance profile ray-autoscaler-v1 for use as the default\. diff --git a/python/ray/tests/test_usage_stats.py b/python/ray/tests/test_usage_stats.py index a05f81ca6..d3c9bff8b 100644 --- a/python/ray/tests/test_usage_stats.py +++ b/python/ray/tests/test_usage_stats.py @@ -1,9 +1,11 @@ +import os import pytest import sys import ray import pathlib import json import time +import subprocess from dataclasses import asdict from pathlib import Path @@ -65,6 +67,127 @@ def print_dashboard_log(): pprint(contents) +def test_usage_stats_heads_up_message(): + """ + Test usage stats heads-up message is shown in the proper cases. + """ + env = os.environ.copy() + env["RAY_USAGE_STATS_PROMPT_ENABLED"] = "0" + result = subprocess.run( + "ray start --head", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + ) + assert result.returncode == 0 + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stderr.decode( + "utf-8" + ) + + subprocess.run("ray stop --force", shell=True) + + result = subprocess.run( + "ray start --head", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert result.returncode == 0 + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE in result.stderr.decode("utf-8") + + result = subprocess.run( + 'ray start --address="127.0.0.1:6379"', + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert result.returncode == 0 + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stderr.decode( + "utf-8" + ) + + subprocess.run("ray stop --force", shell=True) + + result = subprocess.run( + "ray up xxx.yml", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE in result.stderr.decode("utf-8") + + result = subprocess.run( + "ray exec xxx.yml ls --start", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE in result.stderr.decode("utf-8") + + result = subprocess.run( + "ray exec xxx.yml ls", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stderr.decode( + "utf-8" + ) + + result = subprocess.run( + "ray submit xxx.yml yyy.py --start", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE in result.stderr.decode("utf-8") + + result = subprocess.run( + "ray submit xxx.yml yyy.py", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stderr.decode( + "utf-8" + ) + + result = subprocess.run( + 'python -c "import ray; ray.init()"', + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stdout.decode( + "utf-8" + ) + assert usage_constants.USAGE_STATS_HEADS_UP_MESSAGE not in result.stderr.decode( + "utf-8" + ) + + def test_usage_lib_cluster_metadata_generation(monkeypatch, shutdown_only): with monkeypatch.context() as m: m.setenv("RAY_USAGE_STATS_ENABLED", "1")