anyscale 0.26.64__py3-none-any.whl → 0.26.66__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. anyscale/_private/anyscale_client/common.py +1 -1
  2. anyscale/_private/docgen/__main__.py +23 -4
  3. anyscale/_private/docgen/generator.py +127 -34
  4. anyscale/_private/docgen/generator_legacy.py +35 -12
  5. anyscale/client/README.md +37 -1
  6. anyscale/client/openapi_client/__init__.py +26 -0
  7. anyscale/client/openapi_client/api/default_api.py +1446 -245
  8. anyscale/client/openapi_client/models/__init__.py +26 -0
  9. anyscale/client/openapi_client/models/baseimagesenum.py +70 -1
  10. anyscale/client/openapi_client/models/cloud_deployment_compute_config.py +29 -1
  11. anyscale/client/openapi_client/models/connection_type.py +99 -0
  12. anyscale/client/openapi_client/models/create_experimental_workspace.py +29 -1
  13. anyscale/client/openapi_client/models/data_catalog.py +281 -0
  14. anyscale/client/openapi_client/models/data_catalog_connection.py +308 -0
  15. anyscale/client/openapi_client/models/data_catalog_connection_status.py +102 -0
  16. anyscale/client/openapi_client/models/data_catalog_provider.py +101 -0
  17. anyscale/client/openapi_client/models/databricks_connection_config.py +152 -0
  18. anyscale/client/openapi_client/models/databricks_connection_info.py +229 -0
  19. anyscale/client/openapi_client/models/databricks_connection_response.py +148 -0
  20. anyscale/client/openapi_client/models/databricks_register_request.py +187 -0
  21. anyscale/client/openapi_client/models/databricksconnectioninfo_response.py +121 -0
  22. anyscale/client/openapi_client/models/databricksconnectionresponse_response.py +121 -0
  23. anyscale/client/openapi_client/models/datacatalog_list_response.py +147 -0
  24. anyscale/client/openapi_client/models/datacatalogconnection_list_response.py +147 -0
  25. anyscale/client/openapi_client/models/decorated_session.py +29 -1
  26. anyscale/client/openapi_client/models/domain_verification.py +181 -0
  27. anyscale/client/openapi_client/models/list_databricks_connections.py +121 -0
  28. anyscale/client/openapi_client/models/o_auth_connection_response.py +229 -0
  29. anyscale/client/openapi_client/models/oauth_auth_url_response.py +121 -0
  30. anyscale/client/openapi_client/models/oauthconnectionresponse_response.py +121 -0
  31. anyscale/client/openapi_client/models/sso_config.py +148 -0
  32. anyscale/client/openapi_client/models/sso_connection.py +148 -0
  33. anyscale/client/openapi_client/models/sso_connection_state.py +100 -0
  34. anyscale/client/openapi_client/models/ssoconfig_response.py +121 -0
  35. anyscale/client/openapi_client/models/supportedbaseimagesenum.py +70 -1
  36. anyscale/client/openapi_client/models/task_summary_config.py +29 -3
  37. anyscale/client/openapi_client/models/task_table_config.py +29 -3
  38. anyscale/client/openapi_client/models/update_workspace_template.py +346 -0
  39. anyscale/client/openapi_client/models/usage_by_cluster_type.py +174 -0
  40. anyscale/client/openapi_client/models/usagebyclustertype_list_response.py +147 -0
  41. anyscale/client/openapi_client/models/validation_status.py +101 -0
  42. anyscale/commands/cloud_commands.py +310 -206
  43. anyscale/controllers/cloud_controller.py +175 -241
  44. anyscale/controllers/cloud_functional_verification_controller.py +6 -3
  45. anyscale/sdk/anyscale_client/models/baseimagesenum.py +70 -1
  46. anyscale/sdk/anyscale_client/models/cloud_deployment_compute_config.py +29 -1
  47. anyscale/sdk/anyscale_client/models/session.py +31 -3
  48. anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +70 -1
  49. anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
  50. anyscale/util.py +1 -1
  51. anyscale/version.py +1 -1
  52. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/METADATA +1 -1
  53. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/RECORD +58 -33
  54. anyscale/commands/cloud_commands_util.py +0 -10
  55. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/WHEEL +0 -0
  56. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/entry_points.txt +0 -0
  57. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/licenses/LICENSE +0 -0
  58. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/licenses/NOTICE +0 -0
  59. {anyscale-0.26.64.dist-info → anyscale-0.26.66.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  from io import StringIO
2
+ import pathlib
2
3
  import re
3
4
  from typing import List, Optional
4
5
 
@@ -7,10 +8,21 @@ import yaml
7
8
 
8
9
  import anyscale
9
10
  from anyscale.cli_logger import BlockLogger
10
- from anyscale.client.openapi_client.models import ClusterManagementStackVersions
11
+ from anyscale.client.openapi_client.models import (
12
+ AWSConfig,
13
+ CloudDeployment,
14
+ CloudProviders,
15
+ ClusterManagementStackVersions,
16
+ FileStorage,
17
+ GCPConfig,
18
+ KubernetesConfig,
19
+ NetworkingMode,
20
+ NFSMountTarget,
21
+ ObjectStorage,
22
+ )
11
23
  from anyscale.client.openapi_client.models.compute_stack import ComputeStack
12
24
  from anyscale.cloud.models import CreateCloudCollaborator, CreateCloudCollaborators
13
- from anyscale.commands import cloud_commands_util, command_examples
25
+ from anyscale.commands import command_examples
14
26
  from anyscale.commands.util import AnyscaleCommand, OptionPromptNull
15
27
  from anyscale.controllers.cloud_controller import CloudController
16
28
  from anyscale.util import (
@@ -231,12 +243,14 @@ def setup_cloud( # noqa: PLR0913
231
243
  @click.option(
232
244
  "--max-items",
233
245
  required=False,
234
- default=20,
246
+ default=None,
235
247
  type=int,
236
- help="Max items to show in list.",
248
+ help="Maximum number of clouds to return. If not specified, all results are returned.",
237
249
  callback=validate_non_negative_arg,
238
250
  )
239
- def list_cloud(name: Optional[str], cloud_id: Optional[str], max_items: int,) -> None:
251
+ def list_cloud(
252
+ name: Optional[str], cloud_id: Optional[str], max_items: Optional[int]
253
+ ) -> None:
240
254
  print(
241
255
  CloudController().list_clouds(
242
256
  cloud_name=name, cloud_id=cloud_id, max_items=max_items
@@ -893,6 +907,12 @@ def cloud_config_update( # noqa: PLR0913
893
907
  "permissions after the cloud is created."
894
908
  ),
895
909
  )
910
+ @click.option(
911
+ "--resource-file",
912
+ "-f",
913
+ help="Path to a YAML file defining a cloud resource. Schema: https://docs.anyscale.com/reference/cloud/#cloudresource.",
914
+ required=False,
915
+ )
896
916
  def register_cloud( # noqa: PLR0913, PLR0912, C901
897
917
  provider: str,
898
918
  region: str,
@@ -933,240 +953,323 @@ def register_cloud( # noqa: PLR0913, PLR0912, C901
933
953
  yes: bool,
934
954
  skip_verifications: bool,
935
955
  enable_auto_add_user: bool,
956
+ resource_file: Optional[str],
936
957
  ) -> None:
937
- missing_args: List[str] = []
938
-
939
- # Validate K8S-only storage flags
940
- if (
941
- persistent_volume_claim or csi_ephemeral_volume_driver
942
- ) and compute_stack != ComputeStack.K8S:
943
- raise click.ClickException(
944
- "--persistent-volume-claim and --csi-ephemeral-volume-driver are only supported with --compute-stack=k8s"
945
- )
958
+ # Load CloudDeployment from the resource file if provided, otherwise build from CLI flags
959
+ if resource_file:
960
+ # Read the spec file.
961
+ path = pathlib.Path(resource_file)
962
+ if not path.exists():
963
+ raise click.ClickException(f"{resource_file} does not exist.")
964
+ if not path.is_file():
965
+ raise click.ClickException(f"{resource_file} is not a file.")
966
+
967
+ spec = yaml.safe_load(path.read_text())
968
+ try:
969
+ cloud_resource = CloudDeployment(**spec)
970
+
971
+ # Convert nested dict objects to model objects
972
+ if cloud_resource.file_storage:
973
+ cloud_resource.file_storage = FileStorage(**cloud_resource.file_storage)
974
+ if cloud_resource.object_storage:
975
+ cloud_resource.object_storage = ObjectStorage(
976
+ **cloud_resource.object_storage
977
+ )
978
+ if cloud_resource.aws_config:
979
+ cloud_resource.aws_config = AWSConfig(**cloud_resource.aws_config)
980
+ if cloud_resource.gcp_config:
981
+ cloud_resource.gcp_config = GCPConfig(**cloud_resource.gcp_config)
982
+ if cloud_resource.kubernetes_config:
983
+ cloud_resource.kubernetes_config = KubernetesConfig(
984
+ **cloud_resource.kubernetes_config
985
+ )
986
+
987
+ except Exception as e: # noqa: BLE001
988
+ raise click.ClickException(f"Failed to parse cloud resource: {e}")
946
989
 
947
- # Validate mutual exclusivity of storage configurations
948
- storage_configs = []
949
- if nfs_mount_target or nfs_mount_path:
950
- storage_configs.append("NFS")
951
- if persistent_volume_claim:
952
- storage_configs.append("persistent volume claim")
953
- if csi_ephemeral_volume_driver:
954
- storage_configs.append("CSI ephemeral volume driver")
955
-
956
- if len(storage_configs) > 1:
957
- raise click.ClickException(
958
- f"Storage configurations are mutually exclusive. Found: {', '.join(storage_configs)}. "
959
- "Please specify only one of: --nfs-mount-target/--nfs-mount-path, --persistent-volume-claim, or --csi-ephemeral-volume-driver"
960
- )
961
-
962
- if provider == "aws":
963
- if s3_bucket_id and not cloud_storage_bucket_name:
964
- cloud_storage_bucket_name = s3_bucket_id
965
- if efs_id and not file_storage_id:
966
- file_storage_id = efs_id
967
- # Check for missing required arguments for AWS clouds,
968
- # based on the compute stack (not all args are required
969
- # on all compute stacks).
970
- required_resources = [
971
- (vpc_id, "--vpc-id", (ComputeStack.VM)),
972
- (subnet_ids, "--subnet-ids", (ComputeStack.VM)),
973
- (anyscale_iam_role_id, "--anyscale-iam-role-id", (ComputeStack.VM),),
974
- (instance_iam_role_id, "--instance-iam-role-id", (ComputeStack.VM)),
975
- (security_group_ids, "--security-group-ids", (ComputeStack.VM)),
976
- (
977
- cloud_storage_bucket_name,
978
- "--cloud-storage-bucket-name",
979
- (ComputeStack.VM, ComputeStack.K8S),
980
- ),
981
- (kubernetes_zones, "--kubernetes-zones", (ComputeStack.K8S)),
982
- (
983
- anyscale_operator_iam_identity,
984
- "--anyscale-operator-iam-identity",
985
- (ComputeStack.K8S),
986
- ),
987
- ]
990
+ else:
991
+ missing_args: List[str] = []
988
992
 
989
- if not allow_optional_file_storage():
990
- required_resources.append(
991
- (file_storage_id, "--file-storage-id", (ComputeStack.VM)),
993
+ # Validate K8S-only storage flags
994
+ if (
995
+ persistent_volume_claim or csi_ephemeral_volume_driver
996
+ ) and compute_stack != ComputeStack.K8S:
997
+ raise click.ClickException(
998
+ "--persistent-volume-claim and --csi-ephemeral-volume-driver are only supported with --compute-stack=k8s"
992
999
  )
993
1000
 
994
- for resource in required_resources:
995
- if compute_stack in resource[2] and resource[0] is None:
996
- missing_args.append(resource[1])
1001
+ # Validate mutual exclusivity of storage configurations
1002
+ storage_configs = []
1003
+ if nfs_mount_target or nfs_mount_path:
1004
+ storage_configs.append("NFS")
1005
+ if persistent_volume_claim:
1006
+ storage_configs.append("persistent volume claim")
1007
+ if csi_ephemeral_volume_driver:
1008
+ storage_configs.append("CSI ephemeral volume driver")
997
1009
 
998
- if len(missing_args) > 0:
999
- raise click.ClickException(f"Please provide a value for {missing_args}")
1000
-
1001
- CloudController().register_aws_cloud(
1002
- region=region,
1003
- compute_stack=compute_stack,
1004
- name=name,
1005
- vpc_id=vpc_id,
1006
- subnet_ids=subnet_ids.split(",") if subnet_ids else [],
1007
- efs_id=file_storage_id,
1008
- anyscale_iam_role_id=anyscale_iam_role_id,
1009
- instance_iam_role_id=instance_iam_role_id,
1010
- security_group_ids=security_group_ids.split(",")
1011
- if security_group_ids
1012
- else [],
1013
- cloud_storage_bucket_name=cloud_storage_bucket_name,
1014
- memorydb_cluster_id=memorydb_cluster_id,
1015
- functional_verify=functional_verify,
1016
- kubernetes_zones=kubernetes_zones.split(",") if kubernetes_zones else [],
1017
- anyscale_operator_iam_identity=anyscale_operator_iam_identity,
1018
- private_network=private_network,
1019
- cluster_management_stack_version=ClusterManagementStackVersions.V2,
1020
- yes=yes,
1021
- skip_verifications=skip_verifications,
1022
- auto_add_user=enable_auto_add_user,
1023
- external_id=external_id,
1024
- persistent_volume_claim=persistent_volume_claim,
1025
- csi_ephemeral_volume_driver=csi_ephemeral_volume_driver,
1026
- )
1027
- elif provider == "gcp":
1028
- if filestore_instance_id and not file_storage_id:
1029
- file_storage_id = filestore_instance_id
1030
- # Keep the parameter naming ({resource}_name or {resource}_id) consistent with GCP to reduce confusion for customers
1031
- # Check if all required parameters are provided
1032
- # memorystore_instance_name and host_project_id are optional for GCP clouds
1033
- required_resources = [
1034
- (project_id, "--project-id", (ComputeStack.VM)),
1035
- (vpc_name, "--vpc-name", (ComputeStack.VM)),
1036
- (subnet_names, "--subnet-names", (ComputeStack.VM)),
1037
- (
1038
- anyscale_service_account_email,
1039
- "--anyscale-service-account-email",
1040
- (ComputeStack.VM),
1041
- ),
1042
- (
1043
- instance_service_account_email,
1044
- "--instance-service-account-email",
1045
- (ComputeStack.VM),
1046
- ),
1047
- (provider_name, "--provider-name", (ComputeStack.VM)),
1048
- (firewall_policy_names, "--firewall-policy-names", (ComputeStack.VM)),
1049
- (
1050
- cloud_storage_bucket_name,
1051
- "--cloud-storage-bucket-name",
1052
- (ComputeStack.VM, ComputeStack.K8S),
1053
- ),
1054
- (kubernetes_zones, "--kubernetes-zones", (ComputeStack.K8S)),
1055
- (
1056
- anyscale_operator_iam_identity,
1057
- "--anyscale-operator-iam-identity",
1058
- (ComputeStack.K8S),
1059
- ),
1060
- ]
1010
+ if len(storage_configs) > 1:
1011
+ raise click.ClickException(
1012
+ f"Storage configurations are mutually exclusive. Found: {', '.join(storage_configs)}. "
1013
+ "Please specify only one of: --nfs-mount-target/--nfs-mount-path, --persistent-volume-claim, or --csi-ephemeral-volume-driver"
1014
+ )
1061
1015
 
1062
- if not allow_optional_file_storage():
1063
- required_resources.extend(
1064
- [
1016
+ if provider == "aws":
1017
+ if s3_bucket_id and not cloud_storage_bucket_name:
1018
+ cloud_storage_bucket_name = s3_bucket_id
1019
+ if efs_id and not file_storage_id:
1020
+ file_storage_id = efs_id
1021
+ # Check for missing required arguments for AWS clouds,
1022
+ # based on the compute stack (not all args are required
1023
+ # on all compute stacks).
1024
+ required_resources = [
1025
+ (vpc_id, "--vpc-id", (ComputeStack.VM)),
1026
+ (subnet_ids, "--subnet-ids", (ComputeStack.VM)),
1027
+ (anyscale_iam_role_id, "--anyscale-iam_role-id", (ComputeStack.VM),),
1028
+ (instance_iam_role_id, "--instance-iam-role-id", (ComputeStack.VM)),
1029
+ (security_group_ids, "--security-group-ids", (ComputeStack.VM)),
1030
+ (
1031
+ cloud_storage_bucket_name,
1032
+ "--cloud-storage-bucket-name",
1033
+ (ComputeStack.VM, ComputeStack.K8S),
1034
+ ),
1035
+ (kubernetes_zones, "--kubernetes-zones", (ComputeStack.K8S)),
1036
+ (
1037
+ anyscale_operator_iam_identity,
1038
+ "--anyscale-operator-iam-identity",
1039
+ (ComputeStack.K8S),
1040
+ ),
1041
+ ]
1042
+
1043
+ if not allow_optional_file_storage():
1044
+ required_resources.append(
1065
1045
  (file_storage_id, "--file-storage-id", (ComputeStack.VM)),
1066
- (filestore_location, "--filestore-location", (ComputeStack.VM)),
1067
- ]
1046
+ )
1047
+
1048
+ for resource in required_resources:
1049
+ if compute_stack in resource[2] and resource[0] is None:
1050
+ missing_args.append(resource[1])
1051
+
1052
+ if len(missing_args) > 0:
1053
+ raise click.ClickException(f"Please provide a value for {missing_args}")
1054
+
1055
+ cloud_resource = CloudDeployment(
1056
+ compute_stack=compute_stack,
1057
+ provider=CloudProviders.AWS,
1058
+ region=region,
1059
+ networking_mode=NetworkingMode.PRIVATE
1060
+ if private_network
1061
+ else NetworkingMode.PUBLIC,
1062
+ object_storage=ObjectStorage(bucket_name=cloud_storage_bucket_name),
1063
+ file_storage=FileStorage(
1064
+ file_storage_id=file_storage_id,
1065
+ persistent_volume_claim=persistent_volume_claim,
1066
+ csi_ephemeral_volume_driver=csi_ephemeral_volume_driver,
1067
+ )
1068
+ if file_storage_id
1069
+ or persistent_volume_claim
1070
+ or csi_ephemeral_volume_driver
1071
+ else None,
1072
+ aws_config=AWSConfig(
1073
+ vpc_id=vpc_id,
1074
+ subnet_ids=subnet_ids.split(",") if subnet_ids else [],
1075
+ security_group_ids=security_group_ids.split(",")
1076
+ if security_group_ids
1077
+ else [],
1078
+ anyscale_iam_role_id=anyscale_iam_role_id,
1079
+ external_id=external_id,
1080
+ cluster_iam_role_id=instance_iam_role_id,
1081
+ memorydb_cluster_name=memorydb_cluster_id,
1082
+ ),
1083
+ kubernetes_config=KubernetesConfig(
1084
+ anyscale_operator_iam_identity=anyscale_operator_iam_identity,
1085
+ zones=kubernetes_zones.split(",") if kubernetes_zones else [],
1086
+ )
1087
+ if compute_stack == ComputeStack.K8S
1088
+ else None,
1068
1089
  )
1069
1090
 
1070
- for resource in required_resources:
1071
- if compute_stack in resource[2] and resource[0] is None:
1072
- missing_args.append(resource[1])
1073
-
1074
- if len(missing_args) > 0:
1075
- raise click.ClickException(f"Please provide a value for {missing_args}")
1076
-
1077
- if project_id and project_id[0].isdigit():
1078
- # project ID should start with a letter
1079
- raise click.ClickException(
1080
- "Please provide a valid project ID. Note that project ID is not project number, see https://cloud.google.com/resource-manager/docs/creating-managing-projects#before_you_begin for details."
1091
+ elif provider == "gcp":
1092
+ if filestore_instance_id and not file_storage_id:
1093
+ file_storage_id = filestore_instance_id
1094
+ # Keep the parameter naming ({resource}_name or {resource}_id) consistent with GCP to reduce confusion for customers
1095
+ # Check if all required parameters are provided
1096
+ # memorystore_instance_name and host_project_id are optional for GCP clouds
1097
+ required_resources = [
1098
+ (project_id, "--project-id", (ComputeStack.VM)),
1099
+ (vpc_name, "--vpc-name", (ComputeStack.VM)),
1100
+ (subnet_names, "--subnet-names", (ComputeStack.VM)),
1101
+ (
1102
+ anyscale_service_account_email,
1103
+ "--anyscale-service-account-email",
1104
+ (ComputeStack.VM),
1105
+ ),
1106
+ (
1107
+ instance_service_account_email,
1108
+ "--instance-service-account-email",
1109
+ (ComputeStack.VM),
1110
+ ),
1111
+ (provider_name, "--provider-name", (ComputeStack.VM)),
1112
+ (firewall_policy_names, "--firewall-policy-names", (ComputeStack.VM)),
1113
+ (
1114
+ cloud_storage_bucket_name,
1115
+ "--cloud-storage-bucket-name",
1116
+ (ComputeStack.VM, ComputeStack.K8S),
1117
+ ),
1118
+ (kubernetes_zones, "--kubernetes-zones", (ComputeStack.K8S)),
1119
+ (
1120
+ anyscale_operator_iam_identity,
1121
+ "--anyscale-operator-iam-identity",
1122
+ (ComputeStack.K8S),
1123
+ ),
1124
+ ]
1125
+
1126
+ if not allow_optional_file_storage():
1127
+ required_resources.extend(
1128
+ [
1129
+ (file_storage_id, "--file-storage-id", (ComputeStack.VM)),
1130
+ (filestore_location, "--filestore-location", (ComputeStack.VM)),
1131
+ ]
1132
+ )
1133
+
1134
+ for resource in required_resources:
1135
+ if compute_stack in resource[2] and resource[0] is None:
1136
+ missing_args.append(resource[1])
1137
+
1138
+ if len(missing_args) > 0:
1139
+ raise click.ClickException(f"Please provide a value for {missing_args}")
1140
+
1141
+ cloud_resource = CloudDeployment(
1142
+ compute_stack=compute_stack,
1143
+ provider=CloudProviders.GCP,
1144
+ region=region,
1145
+ networking_mode=NetworkingMode.PRIVATE
1146
+ if private_network
1147
+ else NetworkingMode.PUBLIC,
1148
+ object_storage=ObjectStorage(bucket_name=cloud_storage_bucket_name),
1149
+ file_storage=FileStorage(
1150
+ file_storage_id="projects/{}/locations/{}/instances/{}".format(
1151
+ project_id, filestore_location, file_storage_id
1152
+ )
1153
+ if file_storage_id
1154
+ else None,
1155
+ persistent_volume_claim=persistent_volume_claim,
1156
+ csi_ephemeral_volume_driver=csi_ephemeral_volume_driver,
1157
+ )
1158
+ if file_storage_id
1159
+ or persistent_volume_claim
1160
+ or csi_ephemeral_volume_driver
1161
+ else None,
1162
+ gcp_config=GCPConfig(
1163
+ project_id=project_id,
1164
+ host_project_id=host_project_id,
1165
+ provider_name=provider_name,
1166
+ vpc_name=vpc_name,
1167
+ subnet_names=subnet_names.split(",") if subnet_names else [],
1168
+ firewall_policy_names=firewall_policy_names.split(",")
1169
+ if firewall_policy_names
1170
+ else [],
1171
+ anyscale_service_account_email=anyscale_service_account_email,
1172
+ cluster_service_account_email=instance_service_account_email,
1173
+ memorystore_instance_name=memorystore_instance_name,
1174
+ ),
1175
+ kubernetes_config=KubernetesConfig(
1176
+ anyscale_operator_iam_identity=anyscale_operator_iam_identity,
1177
+ zones=kubernetes_zones.split(",") if kubernetes_zones else [],
1178
+ )
1179
+ if compute_stack == ComputeStack.K8S
1180
+ else None,
1081
1181
  )
1082
1182
 
1083
- if (
1084
- compute_stack != ComputeStack.K8S
1085
- and re.search(
1086
- "projects/[0-9]*/locations/global/workloadIdentityPools/.+/providers/.+",
1087
- provider_name,
1088
- )
1089
- is None
1090
- ):
1091
- raise click.ClickException(
1092
- "Please provide a valid, fully qualified provider name. Example: projects/<project number>/locations/global/workloadIdentityPools/<pool name>/providers/<provider id>"
1183
+ elif provider in ("azure", "generic"):
1184
+ # For the 'generic' provider type, for the time being, most fields are optional; only 'name', 'provider', and 'compute-stack' are required.
1185
+ if not name:
1186
+ raise click.ClickException("Please provide a value for --name.")
1187
+
1188
+ if compute_stack != ComputeStack.K8S:
1189
+ raise click.ClickException(
1190
+ "--compute-stack=k8s must be passed to register this Anyscale cloud."
1191
+ )
1192
+
1193
+ # Handle parsing / conversion of nfs_mount_targets.
1194
+ mount_targets: List[NFSMountTarget] = []
1195
+ for target in nfs_mount_target or []:
1196
+ parts = [part.strip() for part in target.split(",")]
1197
+ if len(parts) == 1:
1198
+ mount_targets.append(NFSMountTarget(address=parts[0]))
1199
+ elif len(parts) == 2:
1200
+ mount_targets.append(
1201
+ NFSMountTarget(address=parts[1], zone=parts[0])
1202
+ )
1203
+ else:
1204
+ raise click.ClickException(
1205
+ f"Invalid mount target {target}; expected (zone,address) tuple or a singular address."
1206
+ )
1207
+
1208
+ cloud_provider = (
1209
+ CloudProviders.AZURE if provider == "azure" else CloudProviders.GENERIC
1093
1210
  )
1094
1211
 
1095
- if (
1096
- memorystore_instance_name is not None
1097
- and not cloud_commands_util.validate_memorystore_instance_name(
1098
- memorystore_instance_name
1099
- )
1100
- ):
1101
- raise click.ClickException(
1102
- "Please provide a valid memorystore instance name. Example: projects/<project number>/locations/<location>/instances/<instance id>"
1212
+ cloud_resource = CloudDeployment(
1213
+ compute_stack=ComputeStack.K8S,
1214
+ provider=cloud_provider,
1215
+ region=region or "default",
1216
+ object_storage=ObjectStorage(
1217
+ bucket_name=cloud_storage_bucket_name,
1218
+ region=cloud_storage_bucket_region or region,
1219
+ endpoint=cloud_storage_bucket_endpoint,
1220
+ )
1221
+ if cloud_storage_bucket_name
1222
+ else None,
1223
+ file_storage=FileStorage(
1224
+ mount_targets=mount_targets,
1225
+ mount_path=nfs_mount_path,
1226
+ persistent_volume_claim=persistent_volume_claim,
1227
+ csi_ephemeral_volume_driver=csi_ephemeral_volume_driver,
1228
+ )
1229
+ if mount_targets
1230
+ or persistent_volume_claim
1231
+ or csi_ephemeral_volume_driver
1232
+ else None,
1233
+ kubernetes_config=KubernetesConfig(
1234
+ zones=kubernetes_zones.split(",") if kubernetes_zones else [],
1235
+ ),
1103
1236
  )
1104
1237
 
1105
- if host_project_id is not None and host_project_id[0].isdigit():
1106
- # project ID should start with a letter
1238
+ else:
1107
1239
  raise click.ClickException(
1108
- "Please provide a valid project ID for `--host-project-id`. Note that project ID is not project number, see https://cloud.google.com/resource-manager/docs/creating-managing-projects#before_you_begin for details."
1240
+ f"Invalid Cloud provider: {provider}. Available providers are [aws, gcp, azure, generic]."
1109
1241
  )
1110
1242
 
1243
+ if provider == "aws":
1244
+ CloudController().register_aws_cloud(
1245
+ name=name,
1246
+ cloud_resource=cloud_resource,
1247
+ functional_verify=functional_verify,
1248
+ cluster_management_stack_version=ClusterManagementStackVersions.V2,
1249
+ yes=yes,
1250
+ skip_verifications=skip_verifications,
1251
+ auto_add_user=enable_auto_add_user,
1252
+ )
1253
+ elif provider == "gcp":
1111
1254
  CloudController().register_gcp_cloud(
1112
- region=region,
1113
1255
  name=name,
1114
- compute_stack=compute_stack,
1115
- project_id=project_id,
1116
- vpc_name=vpc_name,
1117
- subnet_names=subnet_names.split(",") if subnet_names else [],
1118
- filestore_instance_id=file_storage_id,
1119
- filestore_location=filestore_location,
1120
- anyscale_service_account_email=anyscale_service_account_email,
1121
- instance_service_account_email=instance_service_account_email,
1122
- # TODO (allenyin): use provider_name instead of provider_id everywhere.
1123
- provider_id=provider_name,
1124
- firewall_policy_names=firewall_policy_names.split(",")
1125
- if firewall_policy_names
1126
- else [],
1127
- cloud_storage_bucket_name=cloud_storage_bucket_name,
1128
- memorystore_instance_name=memorystore_instance_name,
1129
- host_project_id=host_project_id,
1130
- kubernetes_zones=kubernetes_zones.split(",") if kubernetes_zones else [],
1131
- anyscale_operator_iam_identity=anyscale_operator_iam_identity,
1256
+ cloud_resource=cloud_resource,
1132
1257
  functional_verify=functional_verify,
1133
- private_network=private_network,
1134
1258
  cluster_management_stack_version=ClusterManagementStackVersions.V2,
1135
1259
  yes=yes,
1136
1260
  skip_verifications=skip_verifications,
1137
1261
  auto_add_user=enable_auto_add_user,
1138
- persistent_volume_claim=persistent_volume_claim,
1139
- csi_ephemeral_volume_driver=csi_ephemeral_volume_driver,
1140
1262
  )
1141
1263
  elif provider in ("azure", "generic"):
1142
- # For the 'generic' provider type, for the time being, most fields are optional; only 'name', 'provider', and 'compute-stack' are required.
1143
- if not name:
1144
- raise click.ClickException("Please provide a value for --name.")
1145
-
1146
- if compute_stack != ComputeStack.K8S:
1147
- raise click.ClickException(
1148
- "--compute-stack=k8s must be passed to register this Anyscale cloud."
1149
- )
1150
-
1151
1264
  CloudController().register_azure_or_generic_cloud(
1152
1265
  name=name,
1153
1266
  provider=provider,
1267
+ cloud_resource=cloud_resource,
1154
1268
  auto_add_user=enable_auto_add_user,
1155
- region=region,
1156
- cloud_storage_bucket_name=cloud_storage_bucket_name,
1157
- cloud_storage_bucket_endpoint=cloud_storage_bucket_endpoint,
1158
- cloud_storage_bucket_region=cloud_storage_bucket_region,
1159
- nfs_mount_targets=list(nfs_mount_target) if nfs_mount_target else [],
1160
- nfs_mount_path=nfs_mount_path,
1161
- persistent_volume_claim=persistent_volume_claim,
1162
- csi_ephemeral_volume_driver=csi_ephemeral_volume_driver,
1163
- kubernetes_zones=kubernetes_zones.split(",") if kubernetes_zones else [],
1164
- anyscale_operator_iam_identity=anyscale_operator_iam_identity,
1165
1269
  )
1166
-
1167
1270
  else:
1168
1271
  raise click.ClickException(
1169
- f"Invalid Cloud provider: {provider}. Available providers are [aws, gcp]."
1272
+ f"Invalid Cloud provider: {provider}. Available providers are [aws, gcp, azure, generic]."
1170
1273
  )
1171
1274
 
1172
1275
 
@@ -1325,9 +1428,10 @@ def cloud_edit( # noqa: PLR0913
1325
1428
  )
1326
1429
  if (
1327
1430
  memorystore_instance_name is not None
1328
- and not cloud_commands_util.validate_memorystore_instance_name(
1329
- memorystore_instance_name
1431
+ and re.search(
1432
+ "projects/.+/locations/.+/instances/.+", memorystore_instance_name
1330
1433
  )
1434
+ is None
1331
1435
  ):
1332
1436
  raise click.ClickException(
1333
1437
  "Please provide a valid memorystore instance name. Example: projects/<project number>/locations/<location>/instances/<instance id>"