anyscale 0.26.47__py3-none-any.whl → 0.26.48__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 (73) hide show
  1. anyscale/__init__.py +0 -7
  2. anyscale/_private/anyscale_client/anyscale_client.py +1 -208
  3. anyscale/_private/anyscale_client/common.py +0 -55
  4. anyscale/_private/anyscale_client/fake_anyscale_client.py +19 -46
  5. anyscale/_private/docgen/__main__.py +24 -45
  6. anyscale/_private/docgen/generator.py +32 -16
  7. anyscale/_private/docgen/generator_legacy.py +58 -6
  8. anyscale/_private/docgen/models.md +3 -2
  9. anyscale/_private/workload/workload_config.py +16 -8
  10. anyscale/_private/workload/workload_sdk.py +22 -5
  11. anyscale/client/README.md +4 -1
  12. anyscale/client/openapi_client/__init__.py +2 -1
  13. anyscale/client/openapi_client/api/default_api.py +253 -4
  14. anyscale/client/openapi_client/models/__init__.py +2 -1
  15. anyscale/client/openapi_client/models/{alert_type.py → alert_issue_type.py} +8 -20
  16. anyscale/client/openapi_client/models/baseimagesenum.py +1 -2
  17. anyscale/client/openapi_client/models/cloud.py +31 -3
  18. anyscale/client/openapi_client/models/cloud_deployment.py +30 -3
  19. anyscale/client/openapi_client/models/cloud_with_cloud_resource.py +29 -1
  20. anyscale/client/openapi_client/models/cloud_with_cloud_resource_gcp.py +29 -1
  21. anyscale/client/openapi_client/models/dataset_metrics.py +6 -6
  22. anyscale/client/openapi_client/models/dataset_state.py +2 -1
  23. anyscale/client/openapi_client/models/describe_system_workload_response.py +32 -6
  24. anyscale/client/openapi_client/models/experimental_workspace.py +29 -1
  25. anyscale/client/openapi_client/models/experimental_workspaces_sort_field.py +2 -1
  26. anyscale/client/openapi_client/models/operator_metrics.py +8 -9
  27. anyscale/client/openapi_client/models/operator_status.py +102 -0
  28. anyscale/client/openapi_client/models/organization_usage_alert.py +20 -20
  29. anyscale/client/openapi_client/models/supportedbaseimagesenum.py +1 -2
  30. anyscale/cloud/models.py +330 -0
  31. anyscale/commands/cloud_commands.py +132 -43
  32. anyscale/commands/command_examples.py +54 -134
  33. anyscale/commands/compute_config_commands.py +7 -11
  34. anyscale/compute_config/__init__.py +2 -16
  35. anyscale/compute_config/_private/compute_config_sdk.py +27 -17
  36. anyscale/compute_config/commands.py +14 -44
  37. anyscale/compute_config/models.py +49 -26
  38. anyscale/controllers/cloud_controller.py +289 -171
  39. anyscale/controllers/cloud_file_storage_utils.py +204 -0
  40. anyscale/controllers/kubernetes_verifier.py +1567 -0
  41. anyscale/job/_private/job_sdk.py +17 -8
  42. anyscale/job/models.py +1 -1
  43. anyscale/scripts.py +0 -2
  44. anyscale/sdk/anyscale_client/models/baseimagesenum.py +1 -2
  45. anyscale/sdk/anyscale_client/models/cloud.py +31 -3
  46. anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +1 -2
  47. anyscale/shared_anyscale_utils/utils/id_gen.py +1 -0
  48. anyscale/version.py +1 -1
  49. anyscale/workspace/models.py +14 -7
  50. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/METADATA +1 -1
  51. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/RECORD +56 -70
  52. anyscale/commands/llm/dataset_commands.py +0 -269
  53. anyscale/commands/llm/group.py +0 -15
  54. anyscale/commands/llm/models_commands.py +0 -123
  55. anyscale/controllers/llm/__init__.py +0 -0
  56. anyscale/controllers/llm/models_controller.py +0 -144
  57. anyscale/llm/__init__.py +0 -2
  58. anyscale/llm/dataset/__init__.py +0 -2
  59. anyscale/llm/dataset/_private/__init__.py +0 -0
  60. anyscale/llm/dataset/_private/docs.py +0 -63
  61. anyscale/llm/dataset/_private/models.py +0 -71
  62. anyscale/llm/dataset/_private/sdk.py +0 -147
  63. anyscale/llm/model/__init__.py +0 -2
  64. anyscale/llm/model/_private/models_sdk.py +0 -62
  65. anyscale/llm/model/commands.py +0 -93
  66. anyscale/llm/model/models.py +0 -171
  67. anyscale/llm/model/sdk.py +0 -62
  68. anyscale/llm/sdk.py +0 -27
  69. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/WHEEL +0 -0
  70. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/entry_points.txt +0 -0
  71. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/licenses/LICENSE +0 -0
  72. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/licenses/NOTICE +0 -0
  73. {anyscale-0.26.47.dist-info → anyscale-0.26.48.dist-info}/top_level.txt +0 -0
@@ -244,21 +244,25 @@ def list_cloud(name: Optional[str], cloud_id: Optional[str], max_items: int,) ->
244
244
  )
245
245
 
246
246
 
247
+ @cloud_cli.group("deployment", help="Manage the configuration for a cloud deployment.")
248
+ def cloud_deployment_group() -> None:
249
+ pass
250
+
251
+
247
252
  @cloud_cli.group("config", help="Manage the configuration for a cloud.")
248
253
  def cloud_config_group() -> None:
249
254
  pass
250
255
 
251
256
 
252
- @cloud_cli.command(
253
- name="add-deployment",
254
- help="Add a new cloud deployment to an existing cloud.",
257
+ @cloud_deployment_group.command(
258
+ name="create",
259
+ help="Create a new cloud deployment in an existing cloud.",
255
260
  cls=AnyscaleCommand,
256
- example=command_examples.CLOUD_ADD_DEPLOYMENT_EXAMPLE,
261
+ example=command_examples.CLOUD_DEPLOYMENT_CREATE_EXAMPLE,
257
262
  )
258
263
  @click.option(
259
- "--cloud-name",
260
- "-n",
261
- help="The name of the cloud to add the new deployment to.",
264
+ "--cloud",
265
+ help="The name of the cloud to create the new deployment in.",
262
266
  type=str,
263
267
  required=True,
264
268
  )
@@ -274,20 +278,82 @@ def cloud_config_group() -> None:
274
278
  @click.option(
275
279
  "--yes", "-y", is_flag=True, default=False, help="Skip asking for confirmation."
276
280
  )
277
- def cloud_add_deployment(
278
- cloud_name: str, file: str, skip_verification: bool, yes: bool,
281
+ def cloud_deployment_create(
282
+ cloud: str, file: str, skip_verification: bool, yes: bool,
279
283
  ) -> None:
280
284
  try:
281
- CloudController().add_cloud_deployment(cloud_name, file, skip_verification, yes)
285
+ CloudController().create_cloud_deployment(cloud, file, skip_verification, yes)
282
286
  except click.ClickException as e:
283
287
  print(e)
284
288
 
285
289
 
286
- @cloud_cli.command(
287
- name="remove-deployment",
290
+ @cloud_deployment_group.command(
291
+ name="get",
292
+ help="Get a cloud deployment for a cloud.",
293
+ cls=AnyscaleCommand,
294
+ example=command_examples.CLOUD_DEPLOYMENT_GET_EXAMPLE,
295
+ )
296
+ @click.option(
297
+ "--cloud",
298
+ help="The name of the cloud that the cloud deployment belongs to.",
299
+ type=str,
300
+ required=True,
301
+ )
302
+ @click.option(
303
+ "--deployment",
304
+ help="The name of the cloud deployment. If not provided, the primary cloud deployment will be returned.",
305
+ type=str,
306
+ required=False,
307
+ )
308
+ def cloud_deployment_get(cloud: str, deployment: Optional[str]) -> None:
309
+ try:
310
+ result = CloudController().get_cloud_deployment_dict_by_name(cloud, deployment)
311
+ print(yaml.dump(result, sort_keys=False))
312
+ except click.ClickException as e:
313
+ print(e)
314
+
315
+
316
+ @cloud_deployment_group.command(
317
+ name="update",
318
+ help="Update a cloud deployment in an existing cloud.",
319
+ cls=AnyscaleCommand,
320
+ example=command_examples.CLOUD_DEPLOYMENT_UPDATE_EXAMPLE,
321
+ )
322
+ @click.option(
323
+ "--cloud",
324
+ help="The name of the cloud that the cloud deployment belongs to.",
325
+ type=str,
326
+ required=True,
327
+ )
328
+ @click.option(
329
+ "--file",
330
+ "-f",
331
+ help="Path to a YAML file defining the cloud deployment. Schema: https://docs.anyscale.com/reference/cloud-api#clouddeployment.", # TODO(janet): check link
332
+ required=True,
333
+ )
334
+ @click.option(
335
+ "--skip-verification",
336
+ is_flag=True,
337
+ default=False,
338
+ help="Skip cloud deployment verification.",
339
+ )
340
+ @click.option(
341
+ "--yes", "-y", is_flag=True, default=False, help="Skip asking for confirmation."
342
+ )
343
+ def cloud_deployment_update(
344
+ cloud: str, file: str, skip_verification: bool, yes: bool
345
+ ) -> None:
346
+ try:
347
+ CloudController().update_cloud_deployment(cloud, file, skip_verification, yes)
348
+ except click.ClickException as e:
349
+ print(e)
350
+
351
+
352
+ @cloud_deployment_group.command(
353
+ name="delete",
288
354
  help="Remove a cloud deployment from an existing cloud.",
289
355
  cls=AnyscaleCommand,
290
- example=command_examples.CLOUD_REMOVE_DEPLOYMENT_EXAMPLE,
356
+ example=command_examples.CLOUD_DEPLOYMENT_DELETE_EXAMPLE,
291
357
  )
292
358
  @click.option(
293
359
  "--cloud",
@@ -304,7 +370,7 @@ def cloud_add_deployment(
304
370
  @click.option(
305
371
  "--yes", "-y", is_flag=True, default=False, help="Skip asking for confirmation."
306
372
  )
307
- def cloud_remove_deployment(cloud: str, deployment: str, yes: bool,) -> None:
373
+ def cloud_deployment_delete(cloud: str, deployment: str, yes: bool,) -> None:
308
374
  try:
309
375
  CloudController().remove_cloud_deployment(cloud, deployment, yes)
310
376
  except click.ClickException as e:
@@ -353,19 +419,6 @@ def cloud_remove_deployment(cloud: str, deployment: str, yes: bool,) -> None:
353
419
  "are manually granted permissions to access the cloud. No existing cloud permissions are altered by specifying this flag."
354
420
  ),
355
421
  )
356
- @click.option(
357
- "--file",
358
- "-f",
359
- help="YAML file containing the updated cloud spec.",
360
- required=False,
361
- hidden=True,
362
- )
363
- @click.option(
364
- "--skip-verification",
365
- is_flag=True,
366
- default=False,
367
- help="Skip cloud deployment verification.",
368
- )
369
422
  def cloud_update( # noqa: PLR0913
370
423
  cloud_name: Optional[str],
371
424
  name: Optional[str],
@@ -374,16 +427,7 @@ def cloud_update( # noqa: PLR0913
374
427
  enable_head_node_fault_tolerance: bool,
375
428
  yes: bool,
376
429
  enable_auto_add_user: Optional[bool],
377
- file: Optional[str],
378
- skip_verification: bool,
379
430
  ) -> None:
380
- if file:
381
- try:
382
- CloudController().update_cloud_deployments(file, skip_verification)
383
- except click.ClickException as e:
384
- print(e)
385
- return
386
-
387
431
  if cloud_name and name and cloud_name != name:
388
432
  raise click.ClickException(
389
433
  "The positional argument CLOUD_NAME and the keyword argument --name "
@@ -432,14 +476,17 @@ def cloud_config_get(
432
476
 
433
477
 
434
478
  @cloud_config_group.command(
435
- "update", help="Update the current configuration for a cloud."
479
+ "update",
480
+ help="Update the current configuration for a cloud.",
481
+ cls=AnyscaleCommand,
482
+ example=command_examples.CLOUD_CONFIG_UPDATE_EXAMPLE,
436
483
  )
437
484
  @click.argument("cloud-name", required=False)
438
485
  @click.option("--name", "-n", help="Update configuration of cloud by name.", type=str)
439
486
  @click.option(
440
487
  "--cloud-id",
441
488
  "--id",
442
- help="Cloud id to get details about. Alternative to cloud name.",
489
+ help="Cloud id to update. Alternative to cloud name.",
443
490
  required=False,
444
491
  )
445
492
  @click.option(
@@ -457,6 +504,12 @@ def cloud_config_get(
457
504
  "extra data transfer cost from the cloud provider by enabling this feature."
458
505
  ),
459
506
  )
507
+ @click.option(
508
+ "--enable-system-cluster/--disable-system-cluster",
509
+ default=None,
510
+ help="Enable or disable system cluster functionality.",
511
+ required=False,
512
+ )
460
513
  @click.option(
461
514
  "--spec-file",
462
515
  type=str,
@@ -468,14 +521,25 @@ def cloud_config_update(
468
521
  name: Optional[str],
469
522
  cloud_id: Optional[str],
470
523
  enable_log_ingestion: Optional[bool],
524
+ enable_system_cluster: Optional[bool],
471
525
  spec_file: Optional[str],
472
526
  ) -> None:
473
- if any([enable_log_ingestion is not None]) and spec_file:
527
+ if cloud_name and name and cloud_name != name:
528
+ raise click.ClickException(
529
+ "The positional argument CLOUD_NAME and the keyword argument --name "
530
+ "were both provided. Please only provide one of these two arguments."
531
+ )
532
+
533
+ passed_enable_disable_flags = any(
534
+ [enable_log_ingestion is not None, enable_system_cluster is not None]
535
+ )
536
+ if passed_enable_disable_flags and spec_file:
474
537
  raise click.ClickException(
475
- "Please provide only one of the following arguments: --enable-log-ingestion, --disable-log-ingestion, --spec-file."
538
+ "Invalid combination of arguments: --spec-file should not be provided with any other enable/disable flags."
476
539
  )
477
540
 
478
- if any([enable_log_ingestion is not None]):
541
+ if passed_enable_disable_flags:
542
+ # Handle log ingestion configuration
479
543
  # TODO: enable_log_ingestion should be unified into cloud deployment config.
480
544
  if enable_log_ingestion is True:
481
545
  consent_message = click.prompt(
@@ -490,7 +554,7 @@ def cloud_config_update(
490
554
  raise click.ClickException(
491
555
  'You must type "consent" to enable log ingestion.'
492
556
  )
493
- if enable_log_ingestion is False:
557
+ elif enable_log_ingestion is False:
494
558
  confirm_response = click.confirm(
495
559
  "--disable-log-ingestion is specified. Please note the logs that's "
496
560
  "already ingested will not be deleted. Existing clusters will not stop"
@@ -506,13 +570,38 @@ def cloud_config_update(
506
570
  cloud_id=cloud_id,
507
571
  enable_log_ingestion=enable_log_ingestion,
508
572
  )
573
+
574
+ # Handle system cluster configuration
575
+ if enable_system_cluster is True:
576
+ confirm_response = click.confirm(
577
+ "--enable-system-cluster is specified. Please note that this will enable "
578
+ "system cluster functionality for the cloud and will incur extra cost. "
579
+ "Are you sure you want to enable system cluster?"
580
+ )
581
+ elif enable_system_cluster is False:
582
+ confirm_response = click.confirm(
583
+ "--disable-system-cluster is specified. This will disable system cluster "
584
+ "functionality for the cloud. Please note that this will not terminate "
585
+ "the system cluster if it is currently running. "
586
+ "Are you sure you want to disable system cluster?"
587
+ )
588
+ if enable_system_cluster is not None and not confirm_response:
589
+ raise click.ClickException(
590
+ f"You must confirm to {'enable' if enable_system_cluster else 'disable'} system cluster."
591
+ )
592
+
593
+ CloudController().update_system_cluster_config(
594
+ cloud_name=cloud_name or name,
595
+ cloud_id=cloud_id,
596
+ system_cluster_enabled=enable_system_cluster,
597
+ )
509
598
  elif spec_file:
510
599
  CloudController().update_cloud_config(
511
600
  cloud_name=cloud_name or name, cloud_id=cloud_id, spec_file=spec_file,
512
601
  )
513
602
  else:
514
603
  raise click.ClickException(
515
- "Please provide at least one of the following arguments: --enable-log-ingestion, --disable-log-ingestion."
604
+ "Please provide at least one of the following arguments: --enable-log-ingestion, --disable-log-ingestion, --enable-system-cluster, --disable-system-cluster, --spec-file."
516
605
  )
517
606
 
518
607
 
@@ -322,135 +322,6 @@ $ anyscale machine-pool detach --name can-testing --cloud my-cloud
322
322
  Detached machine pool 'can-testing' from cloud 'my-cloud'.
323
323
  """
324
324
 
325
- LLM_MODELS_GET_EXAMPLE = """
326
- $ anyscale llm model get --model-id my-model-id
327
- Output
328
- {
329
- 'id': 'my-model-id',
330
- 'base_model_id': 'meta-llama/Meta-Llama-3-8B',
331
- 'storage_uri': 'gs://my_bucket/my_folder',
332
- 'ft_type': 'LORA',
333
- 'cloud_id': 'cld_tffbxe9ia5phqr1unxhz4f7e1e',
334
- 'project_id': 'prj_dqb6ha67zubz3gdlvn2tmmglb8',
335
- 'created_at': 1725563985,
336
- 'creator': 'test@anyscale.com',
337
- 'job_id': 'N/A',
338
- 'workspace_id': 'expwrk_yje3t8twim18iuta9r45gwcgcn',
339
- 'generation_config': {
340
- 'prompt_format': {
341
- 'system': '<|start_header_id|>system<|end_header_id|>\\n\\n{instruction}<|eot_id|>',
342
- 'assistant': '<|start_header_id|>assistant<|end_header_id|>\\n\\n{instruction}<|eot_id|>',
343
- 'trailing_assistant': '<|start_header_id|>assistant<|end_header_id|>\\n\\n',
344
- 'user': '<|start_header_id|>user<|end_header_id|>\\n\\n{instruction}<|eot_id|>',
345
- 'bos': '<|begin_of_text|>',
346
- 'default_system_message': '',
347
- 'add_system_tags_even_if_message_is_empty': False,
348
- 'system_in_user': False,
349
- 'system_in_last_user': False,
350
- 'strip_whitespace': True
351
- },
352
- 'stopping_sequences': None
353
- }
354
- }
355
- """
356
-
357
- LLM_MODELS_LIST_EXAMPLE = """
358
- $ anyscale llm model list --cloud-id cld_1j41ls4gwkga4pwp8nbql6f239 --project_id prj_i4wy1t442cbe2sthxp61dmtkbh --max-items 2
359
- Output
360
- [
361
- {
362
- 'id': 'meta-llama/Meta-Llama-3-8B-Instruct:test:bnkve',
363
- 'base_model_id': 'meta-llama/Meta-Llama-3-8B-Instruct',
364
- 'storage_uri': 's3://anyscale-production-data-cld-1j41ls4gwkga4pwp...',
365
- 'ft_type': 'LORA',
366
- 'cloud_id': 'cld_1j41ls4gwkga4pwp8nbql6f239',
367
- 'project_id': 'prj_i4wy1t442cbe2sthxp61dmtkbh',
368
- 'created_at': 1725572462,
369
- 'creator': 'test@anyscale.com',
370
- 'job_id': 'N/A',
371
- 'workspace_id': 'expwrk_bqld1y579g3clukr49rsnd7i5m',
372
- 'generation_config': '{"prompt_format": {"system": "<|start_header_id|>s...'
373
- },
374
- {
375
- 'id': 'neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8:test:czcal',
376
- 'base_model_id': 'neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8',
377
- 'storage_uri': 'gs://storage-bucket-cld-tffbxe9ia5phqr1unxhz4f7e1e...',
378
- 'ft_type': 'LORA',
379
- 'cloud_id': 'cld_1j41ls4gwkga4pwp8nbql6f239',
380
- 'project_id': 'prj_i4wy1t442cbe2sthxp61dmtkbh',
381
- 'created_at': 1725563985,
382
- 'creator': 'test@anyscale.com',
383
- 'job_id': 'N/A',
384
- 'workspace_id': 'expwrk_yje3t8twim18iuta9r45gwcgcn',
385
- 'generation_config': '{"prompt_format": {"system": "<|start_header_id|>s...'
386
- }
387
- ]
388
- """
389
-
390
- LLM_MODELS_DELETE_EXAMPLE = """
391
- $ anyscale llm model delete --model-id my-model-id
392
- Output
393
- {'id': 'my-model-id', 'deleted_at': 1725572462}
394
- """
395
-
396
- LLM_DATASET_GET_EXAMPLE = """
397
- $ anyscale llm dataset get john_doe/viggo/train.jsonl
398
- Dataset(
399
- id='dataset_123',
400
- name='john_doe/viggo/train.jsonl',
401
- filename='train.jsonl',
402
- storage_uri='s3://anyscale-test-data-cld-123/org_123/cld_123/datasets/dataset_123/3/john_doe/viggo/train.jsonl',
403
- version=3,
404
- num_versions=3,
405
- created_at=datetime.datetime(2024, 1, 1, 0, 0, tzinfo=tzutc()),
406
- creator_id='usr_123',
407
- project_id='prj_123',
408
- cloud_id='cld_123',
409
- description=None
410
- )
411
- """
412
-
413
- LLM_DATASET_UPLOAD_EXAMPLE = """
414
- $ anyscale llm dataset upload path/to/my_dataset.jsonl -n my_first_dataset
415
-
416
- 0:00:00 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.1 MB / 5.1 MB Uploading '/path/to/my_dataset.jsonl'
417
-
418
- Upload complete!
419
-
420
- Dataset(
421
- id='dataset_123',
422
- name='my_first_dataset',
423
- filename='my_dataset.jsonl',
424
- storage_uri='s3://anyscale-test-data-cld-123/org_123/cld_123/datasets/dataset_123/1/my_dataset.jsonl',
425
- version=1,
426
- num_versions=1,
427
- created_at=datetime.datetime(2024, 1, 1, 0, 0, tzinfo=tzutc()),
428
- creator_id='usr_123',
429
- project_id='prj_123',
430
- cloud_id='cld_123',
431
- description=None
432
- )
433
- """
434
-
435
- LLM_DATASET_DOWNLOAD_EXAMPLE = """
436
- $ anyscale llm dataset download train.jsonl
437
- 0:00:00 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 711.0 kB / 711.0 kB Downloading 'train.jsonl'
438
-
439
- Download complete!
440
-
441
- {"messages":[{"content":"hi","role":"user"},{"content":"Hi! How can I help?","role":"assistant"}]}
442
- ...
443
- {"messages":[{"content":"bye","role":"user"},{"content":"Goodbye!","role":"assistant"}]}
444
- """
445
-
446
- LLM_DATASET_LIST_EXAMPLE = """
447
- $ anyscale llm dataset list
448
- ID Name Description Created At Num Versions
449
- --------- --------------------- -------------------------------- ------------------- --------------
450
- dataset_2 second second upload 1/2/2024 12:00 PM 23
451
- dataset_1 first first upload 1/1/2024 12:00 PM 13
452
- """
453
-
454
325
  RESOURCE_QUOTAS_CREATE_EXAMPLE = """
455
326
  $ anyscale resource-quota create -n my-resource-quota --cloud my-cloud --project my-project --user-email someone@myorg.com --num-instances 100 --num-cpus 1000 --num-gpus 50 --num-accelerators A10G 10 --num-accelerators A100-80G 0
456
327
  (anyscale +2.5s) Name: my-resource-quota
@@ -651,9 +522,9 @@ collaborators:
651
522
  permission_level: "readonly"
652
523
  """
653
524
 
654
- CLOUD_ADD_DEPLOYMENT_EXAMPLE = """\
655
- $ anyscale cloud add-deployment --cloud my-cloud --file new-cloud-deployment.yaml
656
- Successfully added deployment my-new-deployment to cloud my-cloud.
525
+ CLOUD_DEPLOYMENT_CREATE_EXAMPLE = """\
526
+ $ anyscale cloud deployment create --cloud my-cloud --file new-cloud-deployment.yaml
527
+ Successfully created cloud deployment my-new-deployment in cloud my-cloud.
657
528
 
658
529
  $ cat new-cloud-deployment.yaml
659
530
  name: my-new-deployment
@@ -676,9 +547,48 @@ aws_config:
676
547
  memorydb_cluster_name: my-memorydb-cluster
677
548
  """
678
549
 
550
+ CLOUD_DEPLOYMENT_GET_EXAMPLE = """\
551
+ $ anyscale cloud deployment get --cloud my-cloud --deployment my-deployment
552
+ name: my-deployment
553
+ provider: AWS
554
+ compute_stack: VM
555
+ region: us-west-2
556
+ networking_mode: PUBLIC
557
+ object_storage:
558
+ bucket_name: s3://my-bucket
559
+ file_storage:
560
+ file_storage_id: fs-123
561
+ aws_config:
562
+ vpc_id: vpc-123
563
+ subnet_ids:
564
+ - subnet-123
565
+ security_group_ids:
566
+ - sg-123
567
+ anyscale_iam_role_id: arn:aws:iam::123456789012:role/anyscale-role-123
568
+ cluster_iam_role_id: arn:aws:iam::123456789012:role/cluster-role-123
569
+ memorydb_cluster_name: my-memorydb-cluster
570
+ """
571
+
679
572
 
680
- CLOUD_REMOVE_DEPLOYMENT_EXAMPLE = """\
681
- $ anyscale cloud remove-deployment --cloud my-cloud --deployment my-deployment
573
+ CLOUD_DEPLOYMENT_UPDATE_EXAMPLE = """\
574
+ $ anyscale cloud deployment update --cloud my-cloud --file updated-cloud-deployment.yaml
575
+ Output
576
+ (anyscale +3.7s) Detected the following changes:
577
+ --- +++ @@ -15,9 +15,9 @@ file_storage_id: fs-123
578
+ -name: my-cloud-deployment
579
+ +name: my-updated-cloud-deployment
580
+ networking_mode: PUBLIC
581
+ object_storage:
582
+ - bucket_name: s3://my-bucket
583
+ + bucket_name: s3://my-updated-bucket
584
+ provider: AWS
585
+ Would you like to proceed with updating this cloud deployment? [y/N]: y
586
+ (anyscale +10.2s) Successfully updated cloud deployment my-updated-cloud-deployment in cloud my-cloud.
587
+ """
588
+
589
+
590
+ CLOUD_DEPLOYMENT_DELETE_EXAMPLE = """\
591
+ $ anyscale cloud deployment delete --cloud my-cloud --deployment my-deployment
682
592
  Output
683
593
  Please confirm that you would like to remove deployment my-deployment from cloud my-cloud. [y/N]: y
684
594
  (anyscale +3.5s) Successfully removed deployment my-deployment from cloud my-cloud!
@@ -723,6 +633,16 @@ is_default: true
723
633
  compute_stack: VM
724
634
  """
725
635
 
636
+ CLOUD_CONFIG_UPDATE_EXAMPLE = """\
637
+ $ anyscale cloud config update --cloud-id cloud_id --enable-log-ingestion --enable-system-cluster
638
+ --enable-log-ingestion is specified. [...] If you are sure you want to enable this feature, please type "consent": consent
639
+ Output
640
+ (anyscale +7.3s) Successfully updated log ingestion configuration for cloud, cloud_id to True
641
+ --enable-system-cluster is specified. [...] Are you sure you want to enable system cluster? [y/N]: y
642
+ Output
643
+ (anyscale +11.4s) Successfully enabled system cluster for cloud cloud_id
644
+ """
645
+
726
646
  CLOUD_TERMINATE_SYSTEM_CLUSTER_EXAMPLE = """\
727
647
  $ anyscale cloud terminate-system-cluster --cloud-id cloud_id --wait
728
648
  (anyscale +1.3s) Waiting for system cluster termination............
@@ -8,7 +8,10 @@ import anyscale
8
8
  from anyscale.cli_logger import BlockLogger
9
9
  from anyscale.commands import command_examples
10
10
  from anyscale.commands.util import AnyscaleCommand, LegacyAnyscaleCommand
11
- from anyscale.compute_config import ComputeConfig, MultiDeploymentComputeConfig
11
+ from anyscale.compute_config.models import (
12
+ compute_config_type_from_yaml,
13
+ ComputeConfigVersion,
14
+ )
12
15
  from anyscale.controllers.compute_config_controller import ComputeConfigController
13
16
  from anyscale.util import validate_non_negative_arg
14
17
 
@@ -105,15 +108,8 @@ def create_compute_config(
105
108
  if compute_config_file is not None:
106
109
  ComputeConfigController().create(compute_config_file, name)
107
110
  elif config_file is not None:
108
- try:
109
- config = ComputeConfig.from_yaml(config_file)
110
- except TypeError:
111
- config = MultiDeploymentComputeConfig.from_yaml(config_file)
112
-
113
- if isinstance(config, ComputeConfig):
114
- anyscale.compute_config.create(config, name=name)
115
- elif isinstance(config, MultiDeploymentComputeConfig):
116
- anyscale.compute_config.create_multi_deployment(config, name=name)
111
+ config = compute_config_type_from_yaml(config_file)
112
+ anyscale.compute_config.create(config, name=name)
117
113
  else:
118
114
  raise click.ClickException(
119
115
  "Either the --config-file flag or [COMPUTE_CONFIG_FILE] argument must be provided."
@@ -251,7 +247,7 @@ def get_compute_config(
251
247
  include_archived=include_archived,
252
248
  )
253
249
  else:
254
- config: ComputeConfig = anyscale.compute_config.get(
250
+ config: ComputeConfigVersion = anyscale.compute_config.get(
255
251
  name=name, _id=cc_id, include_archived=include_archived,
256
252
  )
257
253
  stream = StringIO()
@@ -10,17 +10,15 @@ from anyscale.compute_config.commands import (
10
10
  _ARCHIVE_EXAMPLE,
11
11
  _CREATE_ARG_DOCSTRINGS,
12
12
  _CREATE_EXAMPLE,
13
- _CREATE_MULTI_DEPLOYMENT_ARG_DOCSTRINGS,
14
- _CREATE_MULTI_DEPLOYMENT_EXAMPLE,
15
13
  _GET_ARG_DOCSTRINGS,
16
14
  _GET_EXAMPLE,
17
15
  archive,
18
16
  create,
19
- create_multi_deployment,
20
17
  get,
21
18
  )
22
19
  from anyscale.compute_config.models import (
23
20
  ComputeConfig,
21
+ ComputeConfigType,
24
22
  ComputeConfigVersion,
25
23
  HeadNodeConfig,
26
24
  MultiDeploymentComputeConfig,
@@ -44,7 +42,7 @@ class ComputeConfigSDK:
44
42
  doc_py_example=_CREATE_EXAMPLE, arg_docstrings=_CREATE_ARG_DOCSTRINGS,
45
43
  )
46
44
  def create( # noqa: F811
47
- self, config: ComputeConfig, *, name: Optional[str],
45
+ self, config: ComputeConfigType, *, name: Optional[str],
48
46
  ) -> str:
49
47
  """Create a new version of a compute config.
50
48
 
@@ -53,18 +51,6 @@ class ComputeConfigSDK:
53
51
  full_name, _ = self._private_sdk.create_compute_config(config, name=name)
54
52
  return full_name
55
53
 
56
- @sdk_docs(
57
- doc_py_example=_CREATE_MULTI_DEPLOYMENT_EXAMPLE,
58
- arg_docstrings=_CREATE_MULTI_DEPLOYMENT_ARG_DOCSTRINGS,
59
- )
60
- def create_multi_deployment( # noqa: F811
61
- self, config: MultiDeploymentComputeConfig, *, name: Optional[str],
62
- ) -> str:
63
- full_name, _ = self._private_sdk.create_multi_deployment_compute_config(
64
- config, name=name
65
- )
66
- return full_name
67
-
68
54
  @sdk_docs(
69
55
  doc_py_example=_GET_EXAMPLE, arg_docstrings=_GET_ARG_DOCSTRINGS,
70
56
  )
@@ -16,6 +16,7 @@ from anyscale.cluster_compute import parse_cluster_compute_name_version
16
16
  from anyscale.compute_config.models import (
17
17
  CloudDeployment,
18
18
  ComputeConfig,
19
+ ComputeConfigType,
19
20
  ComputeConfigVersion,
20
21
  HeadNodeConfig,
21
22
  MarketType,
@@ -121,7 +122,7 @@ class PrivateComputeConfigSDK(BaseSDK):
121
122
 
122
123
  return api_models
123
124
 
124
- def _convert_compute_config_to_api_model(
125
+ def _convert_single_deployment_compute_config_to_api_model(
125
126
  self, compute_config: ComputeConfig
126
127
  ) -> CloudDeploymentComputeConfig:
127
128
  # We should only make the head node schedulable when it's the *only* node in the cluster.
@@ -167,7 +168,7 @@ class PrivateComputeConfigSDK(BaseSDK):
167
168
  )
168
169
 
169
170
  def create_compute_config(
170
- self, compute_config: ComputeConfig, *, name: Optional[str] = None
171
+ self, compute_config: ComputeConfigType, *, name: Optional[str] = None
171
172
  ) -> Tuple[str, str]:
172
173
  """Register the provided compute config and return its internal ID."""
173
174
 
@@ -179,10 +180,27 @@ class PrivateComputeConfigSDK(BaseSDK):
179
180
  "The latest version tag will be generated and returned."
180
181
  )
181
182
 
183
+ if isinstance(compute_config, MultiDeploymentComputeConfig):
184
+ return self.create_multi_deployment_compute_config(
185
+ compute_config, name=name
186
+ )
187
+ else:
188
+ assert isinstance(compute_config, ComputeConfig)
189
+ return self.create_single_deployment_compute_config(
190
+ compute_config, name=name
191
+ )
192
+
193
+ def create_single_deployment_compute_config(
194
+ self, compute_config: ComputeConfig, *, name: Optional[str] = None,
195
+ ) -> Tuple[str, str]:
196
+ """Register the provided single-deployment compute config and return its internal ID."""
197
+
182
198
  # Returns the default cloud if user-provided cloud is not specified (`None`).
183
199
  cloud_id = self.client.get_cloud_id(cloud_name=compute_config.cloud) # type: ignore
184
200
 
185
- deployment_config = self._convert_compute_config_to_api_model(compute_config)
201
+ deployment_config = self._convert_single_deployment_compute_config_to_api_model(
202
+ compute_config
203
+ )
186
204
 
187
205
  compute_config_api_model = ComputeTemplateConfig(
188
206
  cloud_id=cloud_id,
@@ -214,14 +232,6 @@ class PrivateComputeConfigSDK(BaseSDK):
214
232
  name: Optional[str] = None,
215
233
  ) -> Tuple[str, str]:
216
234
  """Register the provided multi-deployment compute config and return its internal ID."""
217
- if name is not None:
218
- _, version = parse_cluster_compute_name_version(name)
219
- if version is not None:
220
- raise ValueError(
221
- "A version tag cannot be provided when creating a compute config. "
222
- "The latest version tag will be generated and returned."
223
- )
224
-
225
235
  # Returns the default cloud if user-provided cloud is not specified (`None`).
226
236
  cloud_id = self.client.get_cloud_id(cloud_name=compute_config.cloud) # type: ignore
227
237
 
@@ -230,7 +240,9 @@ class PrivateComputeConfigSDK(BaseSDK):
230
240
  deployment_configs = []
231
241
  for config in compute_config.configs:
232
242
  assert isinstance(config, ComputeConfig)
233
- deployment_configs.append(self._convert_compute_config_to_api_model(config))
243
+ deployment_configs.append(
244
+ self._convert_single_deployment_compute_config_to_api_model(config)
245
+ )
234
246
  default_config = deployment_configs[0]
235
247
 
236
248
  compute_config_api_model = ComputeTemplateConfig(
@@ -369,7 +381,7 @@ class PrivateComputeConfigSDK(BaseSDK):
369
381
 
370
382
  return configs
371
383
 
372
- def _convert_cloud_deployment_compute_config_api_model_to_compute_config(
384
+ def _convert_cloud_deployment_compute_config_api_model_to_single_deployment_compute_config(
373
385
  self, cloud_name: str, api_model: CloudDeploymentComputeConfig,
374
386
  ) -> ComputeConfig:
375
387
  worker_nodes = None
@@ -431,7 +443,7 @@ class PrivateComputeConfigSDK(BaseSDK):
431
443
  configs = None
432
444
  if api_model_config.deployment_configs:
433
445
  configs = [
434
- self._convert_cloud_deployment_compute_config_api_model_to_compute_config(
446
+ self._convert_cloud_deployment_compute_config_api_model_to_single_deployment_compute_config(
435
447
  cloud.name, config
436
448
  )
437
449
  for config in api_model_config.deployment_configs
@@ -446,9 +458,7 @@ class PrivateComputeConfigSDK(BaseSDK):
446
458
  return ComputeConfigVersion(
447
459
  name=f"{api_model.name}:{api_model.version}",
448
460
  id=api_model.id,
449
- multi_deployment_config=MultiDeploymentComputeConfig(
450
- cloud=cloud.name, configs=configs,
451
- ),
461
+ config=MultiDeploymentComputeConfig(cloud=cloud.name, configs=configs),
452
462
  )
453
463
 
454
464
  # If there are no deployment configs, this is a compute config for a single cloud deployment - parse the top-level fields.