diff --git a/epiccli/catalog.py b/epiccli/catalog.py index 745126d..9205ad7 100644 --- a/epiccli/catalog.py +++ b/epiccli/catalog.py @@ -78,11 +78,13 @@ def list_instances(project_config): for instance in sorted_instances: price_per_hour = instance.get('price_per_hour', 0) if price_per_hour > 0: + efa_str = "EFA Supported" if instance.get('efa_supported') else "No EFA" click.echo( f"- {instance['instance_type']}: " f"{instance['vcpus']} vCPUs, " f"{instance['memory_gb']:.2f} GB Memory, " - f"${instance.get('price_per_hour', 0):.4f}/hr" + f"${instance.get('price_per_hour', 0):.4f}/hr, " + f"{efa_str}" ) except requests.exceptions.RequestException as e: diff --git a/epiccli/job.py b/epiccli/job.py index e096c6e..826faa6 100644 --- a/epiccli/job.py +++ b/epiccli/job.py @@ -28,6 +28,21 @@ def create_job(job_json_file, project_config): with open(job_json_file) as f: job_data = json.load(f) + jobs_list = job_data.get("jobs", []) if isinstance(job_data, dict) and "jobs" in job_data else [job_data] + for job_item in jobs_list: + if isinstance(job_item, dict): + tasks = job_item.get("spec", {}).get("tasks", []) + for task in tasks: + partitions = int(task.get("partitions", 1)) + nodes = int(task.get("nodes", 2)) if task.get("task_distribution") == "node" else 1 + + if partitions < nodes: + click.echo(f"Error: Number of partitions ({partitions}) must be greater than or equal to number of nodes ({nodes}).") + return + if partitions % nodes != 0: + click.echo(f"Error: Number of partitions ({partitions}) must be exactly divisible by number of nodes ({nodes}).") + return + response = requests.post(f"{api_url}/job/", headers=headers, json=job_data) response.raise_for_status() job_response = response.json() diff --git a/epiccli/main.py b/epiccli/main.py index 952cb61..3ca9576 100644 --- a/epiccli/main.py +++ b/epiccli/main.py @@ -459,6 +459,9 @@ def create_job_command(job_json_file, project_name): - `instance_types`: A list of instance types to use for the job. If not specified, the default instance types are used. + - `capacity_reservation_id`: (Optional) The ID of a Capacity Reservation (e.g., Capacity Blocks for ML) to target for the job. + - `task_distribution`: (Optional) The task distribution strategy (e.g., "core" or "node"). + - `nodes`: (Optional) The number of nodes to use for the job, typically used when `task_distribution` is "node". - Use the `epic catalog list-instances` command to see the available instance types. .. code-block:: json @@ -492,15 +495,16 @@ def create_job_command(job_json_file, project_name): "spec": { "app_code": "my-openfoam2212", "tasks": [{"reference": "main-task", - "partitions": 32, + "partitions": 64, + "nodes": 2, "runtime": 1, - "task_distribution": "core", + "task_distribution": "node", "memory_gb": 16, - "instance_types": ["m5.xlarge"] + "instance_types": ["c5n.18xlarge"] }] }, "input_data": {"path": "v2212/motorBike"}, - "app_options": {"base_command": "su sudofoam -c '. /usr/lib/openfoam/openfoam2212/etc/bashrc && ls -lta && ./Allclean && ./Allrun'"}, + "app_options": {"base_command": "su sudofoam -c '. /usr/lib/openfoam/openfoam2212/etc/bashrc && ls -lta && cat /tmp/hostfile && mpirun -np 64 -x PATH -x LD_LIBRARY_PATH -x WM_PROJECT_DIR -x FOAM_SETTINGS --hostfile /tmp/hostfile --use-hwthread-cpus simpleFoam -parallel | tee run.log && reconstructPar -latestTime'"}, "cluster": {"queue_code": "batch-single-node"} }] } diff --git a/epiccli_ui/templates/dashboard.html b/epiccli_ui/templates/dashboard.html index cff9a48..cf4c7c2 100644 --- a/epiccli_ui/templates/dashboard.html +++ b/epiccli_ui/templates/dashboard.html @@ -1,5 +1,6 @@ +
@@ -17,69 +18,63 @@ +Monitor and manage your computational jobs
+Monitor and manage your + computational jobs
Loading...
+Loading...
Set the maximum monthly spend for this project
-Set the maximum monthly spend for this project +
+