cloudos-cli 2.50.0__tar.gz → 2.53.0__tar.gz
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.
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/PKG-INFO +47 -1
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/README.md +46 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/__main__.py +211 -3
- cloudos_cli-2.53.0/cloudos_cli/_version.py +1 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/clos.py +162 -34
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/jobs/job.py +243 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/procurement/images.py +1 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli.egg-info/PKG-INFO +47 -1
- cloudos_cli-2.50.0/cloudos_cli/_version.py +0 -1
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/LICENSE +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/configure/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/configure/configure.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/datasets/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/datasets/datasets.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/import_wf/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/import_wf/import_wf.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/jobs/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/link/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/link/link.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/procurement/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/queue/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/queue/queue.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/array_job.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/cloud.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/details.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/errors.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/last_wf.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/requests.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli/utils/resources.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli.egg-info/SOURCES.txt +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli.egg-info/dependency_links.txt +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli.egg-info/entry_points.txt +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli.egg-info/requires.txt +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/cloudos_cli.egg-info/top_level.txt +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/setup.cfg +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/setup.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/tests/__init__.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/tests/functions_for_pytest.py +0 -0
- {cloudos_cli-2.50.0 → cloudos_cli-2.53.0}/tests/test_cli_project_create.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudos_cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.53.0
|
|
4
4
|
Summary: Python package for interacting with CloudOS
|
|
5
5
|
Home-page: https://github.com/lifebit-ai/cloudos-cli
|
|
6
6
|
Author: David Piñeyro
|
|
@@ -615,6 +615,52 @@ Aborting jobs...
|
|
|
615
615
|
```
|
|
616
616
|
|
|
617
617
|
|
|
618
|
+
#### Clone a job with optional parameter overrides
|
|
619
|
+
|
|
620
|
+
The `clone` command allows you to create a new job based on an existing job's configuration, with the ability to override specific parameters. This is useful for re-running jobs with slight modifications without having to specify all parameters from scratch.
|
|
621
|
+
|
|
622
|
+
Basic usage:
|
|
623
|
+
```console
|
|
624
|
+
cloudos job clone \
|
|
625
|
+
--profile MY_PROFILE
|
|
626
|
+
--job-id "60a7b8c9d0e1f2g3h4i5j6k7"
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Clone with parameter overrides:
|
|
630
|
+
```console
|
|
631
|
+
cloudos job clone \
|
|
632
|
+
--profile MY_PROFILE
|
|
633
|
+
--job-id "60a7b8c9d0e1f2g3h4i5j6k7" \
|
|
634
|
+
--job-queue "high-priority-queue" \
|
|
635
|
+
--cost-limit 50.0 \
|
|
636
|
+
--instance-type "c5.2xlarge" \
|
|
637
|
+
--job-name "cloned_analysis_v2" \
|
|
638
|
+
--nextflow-version "24.04.4" \
|
|
639
|
+
--git-branch "dev" \
|
|
640
|
+
--nextflow-profile "production" \
|
|
641
|
+
--do-not-save-logs true \
|
|
642
|
+
--accelerate-file-staging true \
|
|
643
|
+
--workflow-name "updated-workflow" \
|
|
644
|
+
-p "input=s3://new-bucket/input.csv" \
|
|
645
|
+
-p "output_dir=s3://new-bucket/results"
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Available override options:
|
|
649
|
+
- `--job-queue`: Specify a different job queue
|
|
650
|
+
- `--cost-limit`: Set a new cost limit (use -1 for no limit)
|
|
651
|
+
- `--instance-type`: Change the master instance type
|
|
652
|
+
- `--job-name`: Assign a custom name to the cloned job
|
|
653
|
+
- `--nextflow-version`: Use a different Nextflow version
|
|
654
|
+
- `--git-branch`: Switch to a different git branch
|
|
655
|
+
- `--nextflow-profile`: Change the Nextflow profile
|
|
656
|
+
- `--do-not-save-logs`: Enable/disable log saving
|
|
657
|
+
- `--accelerate-file-staging`: Enable/disable fusion filesystem
|
|
658
|
+
- `--workflow-name`: Use a different workflow
|
|
659
|
+
- `-p, --parameter`: Override or add parameters (can be used multiple times)
|
|
660
|
+
|
|
661
|
+
> [!NOTE]
|
|
662
|
+
> Parameters can be overridden or new ones can be added using `-p` option
|
|
663
|
+
|
|
618
664
|
#### Executor support
|
|
619
665
|
|
|
620
666
|
CloudOS supports [AWS batch](https://www.nextflow.io/docs/latest/executor.html?highlight=executors#aws-batch) executor by default.
|
|
@@ -580,6 +580,52 @@ Aborting jobs...
|
|
|
580
580
|
```
|
|
581
581
|
|
|
582
582
|
|
|
583
|
+
#### Clone a job with optional parameter overrides
|
|
584
|
+
|
|
585
|
+
The `clone` command allows you to create a new job based on an existing job's configuration, with the ability to override specific parameters. This is useful for re-running jobs with slight modifications without having to specify all parameters from scratch.
|
|
586
|
+
|
|
587
|
+
Basic usage:
|
|
588
|
+
```console
|
|
589
|
+
cloudos job clone \
|
|
590
|
+
--profile MY_PROFILE
|
|
591
|
+
--job-id "60a7b8c9d0e1f2g3h4i5j6k7"
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
Clone with parameter overrides:
|
|
595
|
+
```console
|
|
596
|
+
cloudos job clone \
|
|
597
|
+
--profile MY_PROFILE
|
|
598
|
+
--job-id "60a7b8c9d0e1f2g3h4i5j6k7" \
|
|
599
|
+
--job-queue "high-priority-queue" \
|
|
600
|
+
--cost-limit 50.0 \
|
|
601
|
+
--instance-type "c5.2xlarge" \
|
|
602
|
+
--job-name "cloned_analysis_v2" \
|
|
603
|
+
--nextflow-version "24.04.4" \
|
|
604
|
+
--git-branch "dev" \
|
|
605
|
+
--nextflow-profile "production" \
|
|
606
|
+
--do-not-save-logs true \
|
|
607
|
+
--accelerate-file-staging true \
|
|
608
|
+
--workflow-name "updated-workflow" \
|
|
609
|
+
-p "input=s3://new-bucket/input.csv" \
|
|
610
|
+
-p "output_dir=s3://new-bucket/results"
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Available override options:
|
|
614
|
+
- `--job-queue`: Specify a different job queue
|
|
615
|
+
- `--cost-limit`: Set a new cost limit (use -1 for no limit)
|
|
616
|
+
- `--instance-type`: Change the master instance type
|
|
617
|
+
- `--job-name`: Assign a custom name to the cloned job
|
|
618
|
+
- `--nextflow-version`: Use a different Nextflow version
|
|
619
|
+
- `--git-branch`: Switch to a different git branch
|
|
620
|
+
- `--nextflow-profile`: Change the Nextflow profile
|
|
621
|
+
- `--do-not-save-logs`: Enable/disable log saving
|
|
622
|
+
- `--accelerate-file-staging`: Enable/disable fusion filesystem
|
|
623
|
+
- `--workflow-name`: Use a different workflow
|
|
624
|
+
- `-p, --parameter`: Override or add parameters (can be used multiple times)
|
|
625
|
+
|
|
626
|
+
> [!NOTE]
|
|
627
|
+
> Parameters can be overridden or new ones can be added using `-p` option
|
|
628
|
+
|
|
583
629
|
#### Executor support
|
|
584
630
|
|
|
585
631
|
CloudOS supports [AWS batch](https://www.nextflow.io/docs/latest/executor.html?highlight=executors#aws-batch) executor by default.
|
|
@@ -101,7 +101,8 @@ def run_cloudos_cli(ctx, debug):
|
|
|
101
101
|
'list': shared_config,
|
|
102
102
|
'logs': shared_config,
|
|
103
103
|
'results': shared_config,
|
|
104
|
-
'details': shared_config
|
|
104
|
+
'details': shared_config,
|
|
105
|
+
'clone': shared_config
|
|
105
106
|
},
|
|
106
107
|
'workflow': {
|
|
107
108
|
'list': shared_config,
|
|
@@ -162,7 +163,8 @@ def run_cloudos_cli(ctx, debug):
|
|
|
162
163
|
'list': shared_config,
|
|
163
164
|
'logs': shared_config,
|
|
164
165
|
'results': shared_config,
|
|
165
|
-
'details': shared_config
|
|
166
|
+
'details': shared_config,
|
|
167
|
+
'clone': shared_config
|
|
166
168
|
},
|
|
167
169
|
'workflow': {
|
|
168
170
|
'list': shared_config,
|
|
@@ -1225,6 +1227,24 @@ def job_details(ctx,
|
|
|
1225
1227
|
@click.option('--archived',
|
|
1226
1228
|
help=('When this flag is used, only archived jobs list is collected.'),
|
|
1227
1229
|
is_flag=True)
|
|
1230
|
+
@click.option('--filter-status',
|
|
1231
|
+
help='Filter jobs by status (e.g., completed, running, failed, aborted).')
|
|
1232
|
+
@click.option('--filter-job-name',
|
|
1233
|
+
help='Filter jobs by job name ( case insensitive ).')
|
|
1234
|
+
@click.option('--filter-project',
|
|
1235
|
+
help='Filter jobs by project name.')
|
|
1236
|
+
@click.option('--filter-workflow',
|
|
1237
|
+
help='Filter jobs by workflow/pipeline name.')
|
|
1238
|
+
@click.option('--last',
|
|
1239
|
+
help=('When workflows are duplicated, use the latest imported workflow (by date).'),
|
|
1240
|
+
is_flag=True)
|
|
1241
|
+
@click.option('--filter-job-id',
|
|
1242
|
+
help='Filter jobs by specific job ID.')
|
|
1243
|
+
@click.option('--filter-only-mine',
|
|
1244
|
+
help='Filter to show only jobs belonging to the current user.',
|
|
1245
|
+
is_flag=True)
|
|
1246
|
+
@click.option('--filter-queue',
|
|
1247
|
+
help='Filter jobs by queue name. Only applies to jobs running in batch environment. Non-batch jobs are preserved in results.')
|
|
1228
1248
|
@click.option('--verbose',
|
|
1229
1249
|
help='Whether to print information messages or not.',
|
|
1230
1250
|
is_flag=True)
|
|
@@ -1246,6 +1266,15 @@ def list_jobs(ctx,
|
|
|
1246
1266
|
last_n_jobs,
|
|
1247
1267
|
page,
|
|
1248
1268
|
archived,
|
|
1269
|
+
filter_status,
|
|
1270
|
+
filter_job_name,
|
|
1271
|
+
filter_project,
|
|
1272
|
+
filter_workflow,
|
|
1273
|
+
last,
|
|
1274
|
+
filter_job_id,
|
|
1275
|
+
filter_only_mine,
|
|
1276
|
+
#filter_owner,
|
|
1277
|
+
filter_queue,
|
|
1249
1278
|
verbose,
|
|
1250
1279
|
disable_ssl_verification,
|
|
1251
1280
|
ssl_cert,
|
|
@@ -1300,7 +1329,15 @@ def list_jobs(ctx,
|
|
|
1300
1329
|
print("[ERROR] last-n-jobs value was not valid. Please use a positive int or 'all'")
|
|
1301
1330
|
raise
|
|
1302
1331
|
|
|
1303
|
-
my_jobs_r = cl.get_job_list(workspace_id, last_n_jobs, page, archived, verify_ssl
|
|
1332
|
+
my_jobs_r = cl.get_job_list(workspace_id, last_n_jobs, page, archived, verify_ssl,
|
|
1333
|
+
filter_status=filter_status,
|
|
1334
|
+
filter_job_name=filter_job_name,
|
|
1335
|
+
filter_project=filter_project,
|
|
1336
|
+
filter_workflow=filter_workflow,
|
|
1337
|
+
filter_job_id=filter_job_id,
|
|
1338
|
+
filter_only_mine=filter_only_mine,
|
|
1339
|
+
filter_queue=filter_queue,
|
|
1340
|
+
last=last)
|
|
1304
1341
|
if len(my_jobs_r) == 0:
|
|
1305
1342
|
if ctx.get_parameter_source('page') == click.core.ParameterSource.DEFAULT:
|
|
1306
1343
|
print('\t[Message] A total of 0 jobs collected. This is likely because your workspace ' +
|
|
@@ -1419,6 +1456,177 @@ def abort_jobs(ctx,
|
|
|
1419
1456
|
print(f"\tJob '{job}' aborted successfully.")
|
|
1420
1457
|
|
|
1421
1458
|
|
|
1459
|
+
@job.command('clone')
|
|
1460
|
+
@click.option('-k',
|
|
1461
|
+
'--apikey',
|
|
1462
|
+
help='Your CloudOS API key',
|
|
1463
|
+
required=True)
|
|
1464
|
+
@click.option('-c',
|
|
1465
|
+
'--cloudos-url',
|
|
1466
|
+
help=(f'The CloudOS url you are trying to access to. Default={CLOUDOS_URL}.'),
|
|
1467
|
+
default=CLOUDOS_URL,
|
|
1468
|
+
required=True)
|
|
1469
|
+
@click.option('--workspace-id',
|
|
1470
|
+
help='The specific CloudOS workspace id.',
|
|
1471
|
+
required=True)
|
|
1472
|
+
@click.option('--project-name',
|
|
1473
|
+
help='The name of a CloudOS project.')
|
|
1474
|
+
@click.option('-p',
|
|
1475
|
+
'--parameter',
|
|
1476
|
+
multiple=True,
|
|
1477
|
+
help=('A single parameter to pass to the job call. It should be in the ' +
|
|
1478
|
+
'following form: parameter_name=parameter_value. E.g.: ' +
|
|
1479
|
+
'-p input=s3://path_to_my_file. You can use this option as many ' +
|
|
1480
|
+
'times as parameters you want to include.'))
|
|
1481
|
+
@click.option('--nextflow-profile',
|
|
1482
|
+
help=('A comma separated string indicating the nextflow profile/s ' +
|
|
1483
|
+
'to use with your job.'))
|
|
1484
|
+
@click.option('--nextflow-version',
|
|
1485
|
+
help=('Nextflow version to use when executing the workflow in CloudOS. ' +
|
|
1486
|
+
'Default=22.10.8.'),
|
|
1487
|
+
type=click.Choice(['22.10.8', '24.04.4', '22.11.1-edge', 'latest']))
|
|
1488
|
+
@click.option('--git-branch',
|
|
1489
|
+
help=('The branch to run for the selected pipeline. ' +
|
|
1490
|
+
'If not specified it defaults to the last commit ' +
|
|
1491
|
+
'of the default branch.'))
|
|
1492
|
+
@click.option('--job-name',
|
|
1493
|
+
help='The name of the job. If not set, will take the name of the cloned job.')
|
|
1494
|
+
@click.option('--do-not-save-logs',
|
|
1495
|
+
help=('Avoids process log saving. If you select this option, your job process ' +
|
|
1496
|
+
'logs will not be stored.'),
|
|
1497
|
+
is_flag=True)
|
|
1498
|
+
@click.option('--job-queue',
|
|
1499
|
+
help=('Name of the job queue to use with a batch job. ' +
|
|
1500
|
+
'In Azure workspaces, this option is ignored.'))
|
|
1501
|
+
@click.option('--instance-type',
|
|
1502
|
+
help=('The type of compute instance to use as master node. ' +
|
|
1503
|
+
'Default=c5.xlarge(aws)|Standard_D4as_v4(azure).'))
|
|
1504
|
+
@click.option('--cost-limit',
|
|
1505
|
+
help='Add a cost limit to your job. Default=30.0 (For no cost limit please use -1).',
|
|
1506
|
+
type=float)
|
|
1507
|
+
@click.option('--job-id',
|
|
1508
|
+
help='The CloudOS job id of the job to be cloned.',
|
|
1509
|
+
required=True)
|
|
1510
|
+
@click.option('--accelerate-file-staging',
|
|
1511
|
+
help='Enables AWS S3 mountpoint for quicker file staging.',
|
|
1512
|
+
is_flag=True)
|
|
1513
|
+
@click.option('--resumable',
|
|
1514
|
+
help='Whether to make the job able to be resumed or not.',
|
|
1515
|
+
is_flag=True)
|
|
1516
|
+
@click.option('--verbose',
|
|
1517
|
+
help='Whether to print information messages or not.',
|
|
1518
|
+
is_flag=True)
|
|
1519
|
+
@click.option('--disable-ssl-verification',
|
|
1520
|
+
help=('Disable SSL certificate verification. Please, remember that this option is ' +
|
|
1521
|
+
'not generally recommended for security reasons.'),
|
|
1522
|
+
is_flag=True)
|
|
1523
|
+
@click.option('--ssl-cert',
|
|
1524
|
+
help='Path to your SSL certificate file.')
|
|
1525
|
+
@click.option('--profile',
|
|
1526
|
+
help='Profile to use from the config file',
|
|
1527
|
+
default=None)
|
|
1528
|
+
@click.pass_context
|
|
1529
|
+
def clone_job(ctx,
|
|
1530
|
+
apikey,
|
|
1531
|
+
cloudos_url,
|
|
1532
|
+
workspace_id,
|
|
1533
|
+
project_name,
|
|
1534
|
+
parameter,
|
|
1535
|
+
nextflow_profile,
|
|
1536
|
+
nextflow_version,
|
|
1537
|
+
git_branch,
|
|
1538
|
+
job_name,
|
|
1539
|
+
do_not_save_logs,
|
|
1540
|
+
job_queue,
|
|
1541
|
+
instance_type,
|
|
1542
|
+
cost_limit,
|
|
1543
|
+
job_id,
|
|
1544
|
+
accelerate_file_staging,
|
|
1545
|
+
resumable,
|
|
1546
|
+
verbose,
|
|
1547
|
+
disable_ssl_verification,
|
|
1548
|
+
ssl_cert,
|
|
1549
|
+
profile):
|
|
1550
|
+
"""Clone an existing job with optional parameter overrides."""
|
|
1551
|
+
profile = profile or ctx.default_map['job']['clone']['profile']
|
|
1552
|
+
|
|
1553
|
+
# Create a dictionary with required and non-required params
|
|
1554
|
+
required_dict = {
|
|
1555
|
+
'apikey': True,
|
|
1556
|
+
'workspace_id': True,
|
|
1557
|
+
'workflow_name': False,
|
|
1558
|
+
'project_name': False,
|
|
1559
|
+
'procurement_id': False
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
# Determine if the user provided all required parameters
|
|
1563
|
+
config_manager = ConfigurationProfile()
|
|
1564
|
+
user_options = (
|
|
1565
|
+
config_manager.load_profile_and_validate_data(
|
|
1566
|
+
ctx,
|
|
1567
|
+
INIT_PROFILE,
|
|
1568
|
+
CLOUDOS_URL,
|
|
1569
|
+
profile=profile,
|
|
1570
|
+
required_dict=required_dict,
|
|
1571
|
+
apikey=apikey,
|
|
1572
|
+
cloudos_url=cloudos_url,
|
|
1573
|
+
workspace_id=workspace_id,
|
|
1574
|
+
project_name=project_name
|
|
1575
|
+
)
|
|
1576
|
+
)
|
|
1577
|
+
apikey = user_options['apikey']
|
|
1578
|
+
cloudos_url = user_options['cloudos_url']
|
|
1579
|
+
workspace_id = user_options['workspace_id']
|
|
1580
|
+
|
|
1581
|
+
verify_ssl = ssl_selector(disable_ssl_verification, ssl_cert)
|
|
1582
|
+
|
|
1583
|
+
print('Cloning job...')
|
|
1584
|
+
if verbose:
|
|
1585
|
+
print('\t...Preparing objects')
|
|
1586
|
+
|
|
1587
|
+
# Create Job object (set dummy values for project_name and workflow_name, since they come from the cloned job)
|
|
1588
|
+
job_obj = jb.Job(cloudos_url, apikey, None, workspace_id, None, None, workflow_id=1234, project_id="None",
|
|
1589
|
+
mainfile=None, importsfile=None,verify=verify_ssl)
|
|
1590
|
+
|
|
1591
|
+
if verbose:
|
|
1592
|
+
print('\tThe following Job object was created:')
|
|
1593
|
+
print('\t' + str(job_obj) + '\n')
|
|
1594
|
+
print(f'\tCloning job {job_id} in workspace: {workspace_id}')
|
|
1595
|
+
|
|
1596
|
+
try:
|
|
1597
|
+
# Clone the job with provided overrides
|
|
1598
|
+
cloned_job_id = job_obj.clone_job(
|
|
1599
|
+
source_job_id=job_id,
|
|
1600
|
+
queue_name=job_queue,
|
|
1601
|
+
cost_limit=cost_limit,
|
|
1602
|
+
master_instance=instance_type,
|
|
1603
|
+
job_name=job_name,
|
|
1604
|
+
nextflow_version=nextflow_version,
|
|
1605
|
+
branch=git_branch,
|
|
1606
|
+
profile=nextflow_profile,
|
|
1607
|
+
do_not_save_logs=do_not_save_logs,
|
|
1608
|
+
use_fusion=accelerate_file_staging,
|
|
1609
|
+
resumable=resumable,
|
|
1610
|
+
# only when explicitly setting --project-name will be overridden, else using the original project
|
|
1611
|
+
project_name=project_name if ctx.get_parameter_source("project_name") == click.core.ParameterSource.COMMANDLINE else None,
|
|
1612
|
+
parameters=list(parameter) if parameter else None,
|
|
1613
|
+
verify=verify_ssl
|
|
1614
|
+
)
|
|
1615
|
+
if verbose:
|
|
1616
|
+
print(f'\tCloned job ID: {cloned_job_id}')
|
|
1617
|
+
|
|
1618
|
+
print(f"Job successfully cloned. New job ID: {cloned_job_id}")
|
|
1619
|
+
|
|
1620
|
+
except BadRequestException as e:
|
|
1621
|
+
if verbose:
|
|
1622
|
+
print(f'\tError details: {e}')
|
|
1623
|
+
raise ValueError(f"Failed to clone job: {e}")
|
|
1624
|
+
except Exception as e:
|
|
1625
|
+
if verbose:
|
|
1626
|
+
print(f'\tError details: {e}')
|
|
1627
|
+
raise ValueError(f"An error occurred while cloning the job: {e}")
|
|
1628
|
+
|
|
1629
|
+
|
|
1422
1630
|
@workflow.command('list')
|
|
1423
1631
|
@click.option('-k',
|
|
1424
1632
|
'--apikey',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.53.0'
|
|
@@ -357,8 +357,13 @@ class Cloudos:
|
|
|
357
357
|
return r
|
|
358
358
|
|
|
359
359
|
def get_job_list(self, workspace_id, last_n_jobs=30, page=1, archived=False,
|
|
360
|
-
verify=True
|
|
361
|
-
|
|
360
|
+
verify=True, filter_status=None, filter_job_name=None,
|
|
361
|
+
filter_project=None, filter_workflow=None, filter_job_id=None,
|
|
362
|
+
filter_only_mine=False, filter_owner=None, filter_queue=None, last=False):
|
|
363
|
+
"""Get jobs from a CloudOS workspace with optional filtering.
|
|
364
|
+
|
|
365
|
+
Fetches jobs page by page, applies all filters after fetching.
|
|
366
|
+
Stops when enough jobs are collected or no more jobs are available.
|
|
362
367
|
|
|
363
368
|
Parameters
|
|
364
369
|
----------
|
|
@@ -368,52 +373,170 @@ class Cloudos:
|
|
|
368
373
|
How many of the last jobs from the user to retrieve. You can specify a
|
|
369
374
|
very large int or 'all' to get all user's jobs.
|
|
370
375
|
page : int
|
|
371
|
-
Response page to get.
|
|
376
|
+
Response page to get (ignored when using filters - starts from page 1).
|
|
372
377
|
archived : bool
|
|
373
378
|
When True, only the archived jobs are retrieved.
|
|
374
379
|
verify: [bool|string]
|
|
375
380
|
Whether to use SSL verification or not. Alternatively, if
|
|
376
381
|
a string is passed, it will be interpreted as the path to
|
|
377
382
|
the SSL certificate file.
|
|
383
|
+
filter_status : string, optional
|
|
384
|
+
Filter jobs by status (e.g., 'completed', 'running', 'failed').
|
|
385
|
+
filter_job_name : string, optional
|
|
386
|
+
Filter jobs by name.
|
|
387
|
+
filter_project : string, optional
|
|
388
|
+
Filter jobs by project name (will be resolved to project ID).
|
|
389
|
+
filter_workflow : string, optional
|
|
390
|
+
Filter jobs by workflow name (will be resolved to workflow ID).
|
|
391
|
+
filter_job_id : string, optional
|
|
392
|
+
Filter jobs by specific job ID.
|
|
393
|
+
filter_only_mine : bool, optional
|
|
394
|
+
Filter to show only jobs belonging to the current user.
|
|
395
|
+
filter_queue : string, optional
|
|
396
|
+
Filter jobs by queue name (will be resolved to queue ID).
|
|
397
|
+
Only applies to jobs running in batch environment.
|
|
398
|
+
Non-batch jobs are preserved in results as they don't use queues.
|
|
399
|
+
last : bool, optional
|
|
400
|
+
When workflows are duplicated, use the latest imported workflow (by date).
|
|
378
401
|
|
|
379
402
|
Returns
|
|
380
403
|
-------
|
|
381
404
|
r : list
|
|
382
405
|
A list of dicts, each corresponding to a jobs from the user and the workspace.
|
|
383
406
|
"""
|
|
407
|
+
if not workspace_id or not isinstance(workspace_id, str):
|
|
408
|
+
raise ValueError("Invalid workspace_id: must be a non-empty string")
|
|
409
|
+
|
|
410
|
+
if last_n_jobs != 'all' and (not isinstance(last_n_jobs, int) or last_n_jobs <= 0):
|
|
411
|
+
raise ValueError("last_n_jobs must be a positive integer or 'all'")
|
|
412
|
+
|
|
413
|
+
# Validate filter_status values
|
|
414
|
+
if filter_status:
|
|
415
|
+
valid_statuses = ['completed', 'running', 'failed', 'aborted', 'queued', 'pending', 'initializing']
|
|
416
|
+
if filter_status.lower() not in valid_statuses:
|
|
417
|
+
raise ValueError(f"Invalid filter_status '{filter_status}'. Valid values: {', '.join(valid_statuses)}")
|
|
418
|
+
|
|
384
419
|
headers = {
|
|
385
420
|
"Content-type": "application/json",
|
|
386
421
|
"apikey": self.apikey
|
|
387
422
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
423
|
+
|
|
424
|
+
# Build query parameters for server-side filtering
|
|
425
|
+
params = {
|
|
426
|
+
"teamId": workspace_id,
|
|
427
|
+
"archived.status": str(archived).lower(),
|
|
428
|
+
"limit": 100, # Use a reasonable page size
|
|
429
|
+
"page": 1 # Always start from page 1
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# --- Resolve IDs once (before pagination loop) ---
|
|
433
|
+
|
|
434
|
+
# Add simple server-side filters
|
|
435
|
+
if filter_status:
|
|
436
|
+
params["status"] = filter_status.lower()
|
|
437
|
+
if filter_job_name:
|
|
438
|
+
params["name"] = filter_job_name
|
|
439
|
+
if filter_job_id:
|
|
440
|
+
params["id"] = filter_job_id
|
|
441
|
+
|
|
442
|
+
# Resolve project name to ID
|
|
443
|
+
if filter_project:
|
|
444
|
+
try:
|
|
445
|
+
project_id = self.get_project_id_from_name(workspace_id, filter_project, verify=verify)
|
|
446
|
+
if project_id:
|
|
447
|
+
params["project.id"] = project_id
|
|
448
|
+
else:
|
|
449
|
+
raise ValueError(f"Project '{filter_project}' not found.")
|
|
450
|
+
except Exception as e:
|
|
451
|
+
raise ValueError(f"Error resolving project '{filter_project}': {str(e)}")
|
|
452
|
+
|
|
453
|
+
# Resolve workflow name to ID
|
|
454
|
+
if filter_workflow:
|
|
455
|
+
try:
|
|
456
|
+
workflow_content = self.get_workflow_content(workspace_id, filter_workflow, verify=verify, last=last)
|
|
457
|
+
if workflow_content and workflow_content.get("workflows"):
|
|
458
|
+
# Extract the first (and should be only) workflow from the list
|
|
459
|
+
workflow = workflow_content["workflows"][0]
|
|
460
|
+
workflow_id = workflow.get("_id")
|
|
461
|
+
if workflow_id:
|
|
462
|
+
params["workflow.id"] = workflow_id
|
|
463
|
+
else:
|
|
464
|
+
raise ValueError(f"Workflow '{filter_workflow}' not found.")
|
|
465
|
+
else:
|
|
466
|
+
raise ValueError(f"Workflow '{filter_workflow}' not found.")
|
|
467
|
+
except Exception as e:
|
|
468
|
+
raise ValueError(f"Error resolving workflow '{filter_workflow}': {str(e)}")
|
|
469
|
+
|
|
470
|
+
# Get current user ID for filter_only_mine
|
|
471
|
+
if filter_only_mine:
|
|
472
|
+
try:
|
|
473
|
+
user_info = self.get_user_info(verify=verify)
|
|
474
|
+
user_id = user_info.get("id") or user_info.get("_id")
|
|
475
|
+
if user_id:
|
|
476
|
+
params["user.id"] = user_id
|
|
477
|
+
else:
|
|
478
|
+
raise ValueError("Could not retrieve current user information.")
|
|
479
|
+
except Exception as e:
|
|
480
|
+
raise ValueError(f"Error getting current user info: {str(e)}")
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
# --- Fetch jobs page by page ---
|
|
484
|
+
all_jobs = []
|
|
485
|
+
current_page = 1
|
|
486
|
+
|
|
487
|
+
while True:
|
|
488
|
+
params["page"] = current_page
|
|
489
|
+
|
|
490
|
+
r = retry_requests_get(f"{self.cloudos_url}/api/v2/jobs", params=params, headers=headers, verify=verify)
|
|
491
|
+
if r.status_code >= 400:
|
|
492
|
+
raise BadRequestException(r)
|
|
493
|
+
|
|
494
|
+
content = r.json()
|
|
495
|
+
page_jobs = content.get('jobs', [])
|
|
496
|
+
|
|
497
|
+
# No jobs returned, we've reached the end
|
|
498
|
+
if not page_jobs:
|
|
499
|
+
break
|
|
500
|
+
|
|
501
|
+
all_jobs.extend(page_jobs)
|
|
502
|
+
|
|
503
|
+
# Check if we have enough jobs or reached the last page
|
|
504
|
+
if last_n_jobs != 'all' and len(all_jobs) >= last_n_jobs:
|
|
505
|
+
break
|
|
506
|
+
if len(page_jobs) < params["limit"]:
|
|
507
|
+
break # Last page (fewer jobs than requested page size)
|
|
508
|
+
|
|
509
|
+
current_page += 1
|
|
510
|
+
|
|
511
|
+
# --- Local queue filtering (not supported by API) ---
|
|
512
|
+
if filter_queue:
|
|
513
|
+
try:
|
|
514
|
+
batch_jobs=[job for job in all_jobs if job.get("batch", {})]
|
|
515
|
+
if batch_jobs:
|
|
516
|
+
from cloudos_cli.queue.queue import Queue
|
|
517
|
+
queue_api = Queue(self.cloudos_url, self.apikey, self.cromwell_token, workspace_id, verify)
|
|
518
|
+
queues = queue_api.get_job_queues()
|
|
519
|
+
|
|
520
|
+
queue_id = None
|
|
521
|
+
for queue in queues:
|
|
522
|
+
if queue.get("label") == filter_queue or queue.get("name") == filter_queue:
|
|
523
|
+
queue_id = queue.get("id") or queue.get("_id")
|
|
524
|
+
break
|
|
525
|
+
|
|
526
|
+
if not queue_id:
|
|
527
|
+
raise ValueError(f"Queue with name '{filter_queue}' not found in workspace '{workspace_id}'")
|
|
528
|
+
|
|
529
|
+
all_jobs = [job for job in all_jobs if job.get("batch", {}).get("jobQueue", {}).get("id") == queue_id]
|
|
530
|
+
else:
|
|
531
|
+
raise ValueError(f"The environment is not a batch environment so queues do not exist. Please remove the --filter-queue option.")
|
|
532
|
+
except Exception as e:
|
|
533
|
+
raise ValueError(f"Error filtering by queue '{filter_queue}': {str(e)}")
|
|
534
|
+
|
|
535
|
+
# --- Apply limit after all filtering ---
|
|
536
|
+
if last_n_jobs != 'all' and isinstance(last_n_jobs, int) and last_n_jobs > 0:
|
|
537
|
+
all_jobs = all_jobs[:last_n_jobs]
|
|
538
|
+
|
|
539
|
+
return all_jobs
|
|
417
540
|
|
|
418
541
|
@staticmethod
|
|
419
542
|
def process_job_list(r, all_fields=False):
|
|
@@ -460,7 +583,13 @@ class Cloudos:
|
|
|
460
583
|
if all_fields:
|
|
461
584
|
df = df_full
|
|
462
585
|
else:
|
|
463
|
-
|
|
586
|
+
# Only select columns that actually exist in the DataFrame
|
|
587
|
+
existing_columns = [col for col in COLUMNS if col in df_full.columns]
|
|
588
|
+
if existing_columns:
|
|
589
|
+
df = df_full.loc[:, existing_columns]
|
|
590
|
+
else:
|
|
591
|
+
# If none of the predefined columns exist, raise missing error
|
|
592
|
+
raise ValueError(f"None of the predefined columns {COLUMNS} exist in retrieved columns:{list(df_full.columns)}")
|
|
464
593
|
return df
|
|
465
594
|
|
|
466
595
|
def reorder_job_list(self, my_jobs_df, filename='my_jobs.csv'):
|
|
@@ -1199,7 +1328,6 @@ class Cloudos:
|
|
|
1199
1328
|
response = retry_requests_get(url, headers=headers, verify=verify)
|
|
1200
1329
|
# return all content
|
|
1201
1330
|
content = json.loads(response.content)
|
|
1202
|
-
|
|
1203
1331
|
if response.status_code >= 400:
|
|
1204
1332
|
raise BadRequestException(response)
|
|
1205
1333
|
|
|
@@ -921,3 +921,246 @@ class Job(Cloudos):
|
|
|
921
921
|
"parameterKind": "textValue",
|
|
922
922
|
"textValue": f"{rest}"
|
|
923
923
|
}
|
|
924
|
+
|
|
925
|
+
def get_job_request_payload(self, job_id, verify=True):
|
|
926
|
+
"""Get the original request payload for a job.
|
|
927
|
+
|
|
928
|
+
Parameters
|
|
929
|
+
----------
|
|
930
|
+
job_id : str
|
|
931
|
+
The CloudOS job ID to get the payload for.
|
|
932
|
+
verify : [bool|string]
|
|
933
|
+
Whether to use SSL verification or not. Alternatively, if
|
|
934
|
+
a string is passed, it will be interpreted as the path to
|
|
935
|
+
the SSL certificate file.
|
|
936
|
+
|
|
937
|
+
Returns
|
|
938
|
+
-------
|
|
939
|
+
dict
|
|
940
|
+
The original job request payload.
|
|
941
|
+
"""
|
|
942
|
+
headers = {
|
|
943
|
+
"Content-type": "application/json",
|
|
944
|
+
"apikey": self.apikey
|
|
945
|
+
}
|
|
946
|
+
url = f"{self.cloudos_url}/api/v1/jobs/{job_id}/request-payload?teamId={self.workspace_id}"
|
|
947
|
+
r = retry_requests_get(url, headers=headers, verify=verify)
|
|
948
|
+
if r.status_code >= 400:
|
|
949
|
+
raise BadRequestException(r)
|
|
950
|
+
return json.loads(r.content)
|
|
951
|
+
|
|
952
|
+
def update_parameter_value(self, parameters, param_name, new_value):
|
|
953
|
+
"""Update a parameter value in the parameters list.
|
|
954
|
+
|
|
955
|
+
Parameters
|
|
956
|
+
----------
|
|
957
|
+
parameters : list
|
|
958
|
+
List of parameter dictionaries.
|
|
959
|
+
param_name : str
|
|
960
|
+
Name of the parameter to update.
|
|
961
|
+
new_value : str
|
|
962
|
+
New value for the parameter.
|
|
963
|
+
|
|
964
|
+
Returns
|
|
965
|
+
-------
|
|
966
|
+
bool
|
|
967
|
+
True if parameter was found and updated, False otherwise.
|
|
968
|
+
"""
|
|
969
|
+
for param in parameters:
|
|
970
|
+
if param.get('name') == param_name:
|
|
971
|
+
# Handle different parameter kinds
|
|
972
|
+
if param.get('parameterKind') == 'textValue':
|
|
973
|
+
param['textValue'] = new_value
|
|
974
|
+
elif param.get('parameterKind') == 'dataItem':
|
|
975
|
+
# For data items, we need to process the value to get file/folder ID
|
|
976
|
+
# This is a simplified version - in practice you'd need more logic
|
|
977
|
+
if new_value.startswith('s3://') or new_value.startswith('az://'):
|
|
978
|
+
param['textValue'] = new_value
|
|
979
|
+
param['parameterKind'] = 'textValue'
|
|
980
|
+
else:
|
|
981
|
+
# Try to process as file/data item
|
|
982
|
+
processed_param = self.docker_workflow_param_processing(f"--{param_name}={new_value}", self.project_name)
|
|
983
|
+
param.update(processed_param)
|
|
984
|
+
return True
|
|
985
|
+
return False
|
|
986
|
+
|
|
987
|
+
def clone_job(self,
|
|
988
|
+
source_job_id,
|
|
989
|
+
queue_name=None,
|
|
990
|
+
cost_limit=None,
|
|
991
|
+
master_instance=None,
|
|
992
|
+
job_name=None,
|
|
993
|
+
nextflow_version=None,
|
|
994
|
+
branch=None,
|
|
995
|
+
profile=None,
|
|
996
|
+
do_not_save_logs=None,
|
|
997
|
+
use_fusion=None,
|
|
998
|
+
resumable=None,
|
|
999
|
+
project_name=None,
|
|
1000
|
+
parameters=None,
|
|
1001
|
+
verify=True):
|
|
1002
|
+
"""Clone an existing job with optional parameter overrides.
|
|
1003
|
+
|
|
1004
|
+
Parameters
|
|
1005
|
+
----------
|
|
1006
|
+
source_job_id : str
|
|
1007
|
+
The CloudOS job ID to clone from.
|
|
1008
|
+
queue_name : str, optional
|
|
1009
|
+
Name of the job queue to use.
|
|
1010
|
+
cost_limit : float, optional
|
|
1011
|
+
Job cost limit override.
|
|
1012
|
+
master_instance : str, optional
|
|
1013
|
+
Master instance type override.
|
|
1014
|
+
job_name : str, optional
|
|
1015
|
+
New job name.
|
|
1016
|
+
nextflow_version : str, optional
|
|
1017
|
+
Nextflow version override.
|
|
1018
|
+
branch : str, optional
|
|
1019
|
+
Git branch override.
|
|
1020
|
+
profile : str, optional
|
|
1021
|
+
Nextflow profile override.
|
|
1022
|
+
do_not_save_logs : bool, optional
|
|
1023
|
+
Whether to save logs override.
|
|
1024
|
+
use_fusion : bool, optional
|
|
1025
|
+
Whether to use fusion filesystem override.
|
|
1026
|
+
resumable : bool, optional
|
|
1027
|
+
Whether to make the job resumable or not.
|
|
1028
|
+
project_name : str, optional
|
|
1029
|
+
Project name override (will look up new project ID).
|
|
1030
|
+
parameters : list, optional
|
|
1031
|
+
List of parameter overrides in format ['param1=value1', 'param2=value2'].
|
|
1032
|
+
verify : [bool|string]
|
|
1033
|
+
Whether to use SSL verification or not. Alternatively, if
|
|
1034
|
+
a string is passed, it will be interpreted as the path to
|
|
1035
|
+
the SSL certificate file.
|
|
1036
|
+
|
|
1037
|
+
Returns
|
|
1038
|
+
-------
|
|
1039
|
+
str
|
|
1040
|
+
The CloudOS job ID of the cloned job.
|
|
1041
|
+
"""
|
|
1042
|
+
# Get the original job payload
|
|
1043
|
+
original_payload = self.get_job_request_payload(source_job_id, verify=verify)
|
|
1044
|
+
|
|
1045
|
+
# Create a copy of the payload for modification
|
|
1046
|
+
cloned_payload = json.loads(json.dumps(original_payload))
|
|
1047
|
+
|
|
1048
|
+
# remove unwanted fields
|
|
1049
|
+
del cloned_payload['_id']
|
|
1050
|
+
del cloned_payload['resourceId']
|
|
1051
|
+
|
|
1052
|
+
# Override job name if provided
|
|
1053
|
+
if job_name:
|
|
1054
|
+
cloned_payload['name'] = job_name
|
|
1055
|
+
|
|
1056
|
+
# Override cost limit if provided
|
|
1057
|
+
if cost_limit is not None:
|
|
1058
|
+
if 'execution' not in cloned_payload:
|
|
1059
|
+
cloned_payload['execution'] = {}
|
|
1060
|
+
cloned_payload['execution']['computeCostLimit'] = cost_limit
|
|
1061
|
+
|
|
1062
|
+
# Override master instance if provided
|
|
1063
|
+
if master_instance:
|
|
1064
|
+
if 'masterInstance' not in cloned_payload:
|
|
1065
|
+
cloned_payload['masterInstance'] = {'requestedInstance': {}}
|
|
1066
|
+
cloned_payload['masterInstance']['requestedInstance']['type'] = master_instance
|
|
1067
|
+
|
|
1068
|
+
# Override nextflow version if provided (only for non-docker workflows)
|
|
1069
|
+
if nextflow_version and 'nextflowVersion' in cloned_payload:
|
|
1070
|
+
cloned_payload['nextflowVersion'] = nextflow_version
|
|
1071
|
+
elif nextflow_version and cloned_payload['executionPlatform'] == 'azure' and\
|
|
1072
|
+
nextflow_version not in ['22.11.1-edge', 'latest']:
|
|
1073
|
+
print("[Message]: Azure workspace only uses Nextflow version 22.11.1-edge, option '--nextflow-version' is ignored.\n")
|
|
1074
|
+
|
|
1075
|
+
# Override branch if provided
|
|
1076
|
+
if branch:
|
|
1077
|
+
if 'revision' not in cloned_payload:
|
|
1078
|
+
cloned_payload['revision'] = {}
|
|
1079
|
+
cloned_payload['revision']['revisionType'] = 'branch'
|
|
1080
|
+
cloned_payload['revision']['branch'] = branch
|
|
1081
|
+
# Clear other revision types
|
|
1082
|
+
cloned_payload['revision'].pop('commit', None)
|
|
1083
|
+
cloned_payload['revision'].pop('tag', None)
|
|
1084
|
+
|
|
1085
|
+
# Override profile if provided
|
|
1086
|
+
if profile:
|
|
1087
|
+
cloned_payload['profile'] = profile
|
|
1088
|
+
|
|
1089
|
+
# Override save logs if provided
|
|
1090
|
+
if do_not_save_logs:
|
|
1091
|
+
cloned_payload['saveProcessLogs'] = do_not_save_logs
|
|
1092
|
+
|
|
1093
|
+
# Override use fusion if provided
|
|
1094
|
+
if use_fusion and cloned_payload['executionPlatform'] != 'azure':
|
|
1095
|
+
cloned_payload['usesFusionFileSystem'] = use_fusion
|
|
1096
|
+
elif use_fusion and cloned_payload['executionPlatform'] == 'azure':
|
|
1097
|
+
print("[Message]: Azure workspace does not use fusion filesystem, option '--accelerate-file-staging' is ignored.\n")
|
|
1098
|
+
|
|
1099
|
+
# Override resumable if provided
|
|
1100
|
+
if resumable:
|
|
1101
|
+
cloned_payload['resumable'] = resumable
|
|
1102
|
+
|
|
1103
|
+
# Handle job queue override
|
|
1104
|
+
if queue_name:
|
|
1105
|
+
if cloned_payload['executionPlatform'] != 'azure':
|
|
1106
|
+
try:
|
|
1107
|
+
from cloudos_cli.queue.queue import Queue
|
|
1108
|
+
queue_api = Queue(self.cloudos_url, self.apikey, self.cromwell_token, self.workspace_id, verify)
|
|
1109
|
+
queues = queue_api.get_job_queues()
|
|
1110
|
+
|
|
1111
|
+
queue_id = None
|
|
1112
|
+
for queue in queues:
|
|
1113
|
+
if queue.get("label") == queue_name or queue.get("name") == queue_name:
|
|
1114
|
+
queue_id = queue.get("id") or queue.get("_id")
|
|
1115
|
+
break
|
|
1116
|
+
cloned_payload['batch']['jobQueue'] = queue_id
|
|
1117
|
+
if not queue_id:
|
|
1118
|
+
raise ValueError(f"Queue with name '{queue_name}' not found in workspace '{self.workspace_id}'")
|
|
1119
|
+
except Exception as e:
|
|
1120
|
+
raise ValueError(f"Error filtering by queue '{queue_name}': {str(e)}")
|
|
1121
|
+
else:
|
|
1122
|
+
print("[Message]: Azure workspace does not use job queues, option '--job-queue' is ignored.\n")
|
|
1123
|
+
|
|
1124
|
+
# Handle parameter overrides
|
|
1125
|
+
if parameters:
|
|
1126
|
+
cloned_parameters = cloned_payload.get('parameters', [])
|
|
1127
|
+
for param_override in parameters:
|
|
1128
|
+
param_name, param_value = param_override.split('=', 1)
|
|
1129
|
+
param_name = param_name.lstrip('-') # Remove leading dashes
|
|
1130
|
+
if not self.update_parameter_value(cloned_parameters, param_name, param_value):
|
|
1131
|
+
# Parameter not found, add as new parameter
|
|
1132
|
+
# Determine workflow type to set proper prefix and format
|
|
1133
|
+
prefix = "--" if param_override.startswith('--') else ("-" if param_override.startswith('-') else "")
|
|
1134
|
+
new_param = {
|
|
1135
|
+
"prefix": prefix,
|
|
1136
|
+
"name": param_name,
|
|
1137
|
+
"parameterKind": "textValue",
|
|
1138
|
+
"textValue": param_value
|
|
1139
|
+
}
|
|
1140
|
+
cloned_parameters.append(new_param)
|
|
1141
|
+
cloned_payload['parameters'] = cloned_parameters
|
|
1142
|
+
|
|
1143
|
+
# setup project name
|
|
1144
|
+
if project_name:
|
|
1145
|
+
# get project ID
|
|
1146
|
+
project_id = self.get_project_id_from_name(self.workspace_id, project_name, verify=verify)
|
|
1147
|
+
cloned_payload['project'] = project_id
|
|
1148
|
+
|
|
1149
|
+
# Send the cloned job
|
|
1150
|
+
headers = {
|
|
1151
|
+
"Content-type": "application/json",
|
|
1152
|
+
"apikey": self.apikey
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
r = retry_requests_post(f"{self.cloudos_url}/api/v2/jobs?teamId={self.workspace_id}",
|
|
1156
|
+
data=json.dumps(cloned_payload),
|
|
1157
|
+
headers=headers,
|
|
1158
|
+
verify=verify)
|
|
1159
|
+
|
|
1160
|
+
if r.status_code >= 400:
|
|
1161
|
+
raise BadRequestException(r)
|
|
1162
|
+
|
|
1163
|
+
j_id = json.loads(r.content)["jobId"]
|
|
1164
|
+
print('\tJob successfully cloned and launched to CloudOS, please check the ' +
|
|
1165
|
+
f"following link: {self.cloudos_url}/app/advanced-analytics/analyses/{j_id}\n")
|
|
1166
|
+
return j_id
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudos_cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.53.0
|
|
4
4
|
Summary: Python package for interacting with CloudOS
|
|
5
5
|
Home-page: https://github.com/lifebit-ai/cloudos-cli
|
|
6
6
|
Author: David Piñeyro
|
|
@@ -615,6 +615,52 @@ Aborting jobs...
|
|
|
615
615
|
```
|
|
616
616
|
|
|
617
617
|
|
|
618
|
+
#### Clone a job with optional parameter overrides
|
|
619
|
+
|
|
620
|
+
The `clone` command allows you to create a new job based on an existing job's configuration, with the ability to override specific parameters. This is useful for re-running jobs with slight modifications without having to specify all parameters from scratch.
|
|
621
|
+
|
|
622
|
+
Basic usage:
|
|
623
|
+
```console
|
|
624
|
+
cloudos job clone \
|
|
625
|
+
--profile MY_PROFILE
|
|
626
|
+
--job-id "60a7b8c9d0e1f2g3h4i5j6k7"
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Clone with parameter overrides:
|
|
630
|
+
```console
|
|
631
|
+
cloudos job clone \
|
|
632
|
+
--profile MY_PROFILE
|
|
633
|
+
--job-id "60a7b8c9d0e1f2g3h4i5j6k7" \
|
|
634
|
+
--job-queue "high-priority-queue" \
|
|
635
|
+
--cost-limit 50.0 \
|
|
636
|
+
--instance-type "c5.2xlarge" \
|
|
637
|
+
--job-name "cloned_analysis_v2" \
|
|
638
|
+
--nextflow-version "24.04.4" \
|
|
639
|
+
--git-branch "dev" \
|
|
640
|
+
--nextflow-profile "production" \
|
|
641
|
+
--do-not-save-logs true \
|
|
642
|
+
--accelerate-file-staging true \
|
|
643
|
+
--workflow-name "updated-workflow" \
|
|
644
|
+
-p "input=s3://new-bucket/input.csv" \
|
|
645
|
+
-p "output_dir=s3://new-bucket/results"
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Available override options:
|
|
649
|
+
- `--job-queue`: Specify a different job queue
|
|
650
|
+
- `--cost-limit`: Set a new cost limit (use -1 for no limit)
|
|
651
|
+
- `--instance-type`: Change the master instance type
|
|
652
|
+
- `--job-name`: Assign a custom name to the cloned job
|
|
653
|
+
- `--nextflow-version`: Use a different Nextflow version
|
|
654
|
+
- `--git-branch`: Switch to a different git branch
|
|
655
|
+
- `--nextflow-profile`: Change the Nextflow profile
|
|
656
|
+
- `--do-not-save-logs`: Enable/disable log saving
|
|
657
|
+
- `--accelerate-file-staging`: Enable/disable fusion filesystem
|
|
658
|
+
- `--workflow-name`: Use a different workflow
|
|
659
|
+
- `-p, --parameter`: Override or add parameters (can be used multiple times)
|
|
660
|
+
|
|
661
|
+
> [!NOTE]
|
|
662
|
+
> Parameters can be overridden or new ones can be added using `-p` option
|
|
663
|
+
|
|
618
664
|
#### Executor support
|
|
619
665
|
|
|
620
666
|
CloudOS supports [AWS batch](https://www.nextflow.io/docs/latest/executor.html?highlight=executors#aws-batch) executor by default.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.50.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|