Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions api/PclusterApiHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
USER_ROLES_CLAIM = os.getenv("USER_ROLES_CLAIM", "cognito:groups")
SSM_LOG_GROUP_NAME = os.getenv("SSM_LOG_GROUP_NAME")
ARG_VERSION="version"
ANALYSIS_RESULTS_BASE_PATH = "/fsx/analysis_results/ubuntu"

try:
if (not USER_POOL_ID or USER_POOL_ID == "") and SECRET_ID:
Expand Down Expand Up @@ -435,6 +436,54 @@ def queue_status():
return {"jobs": []} if jobs == "" else {"jobs": json.loads(jobs)}


def analysis_worksets():
user = request.args.get("user", "ec2-user")
instance_id = request.args.get("instance_id")
region = request.args.get("region")

command = (
"python3 - <<\\\"PY\\\"\n"
"import json\n"
"import os\n"
f"base = {ANALYSIS_RESULTS_BASE_PATH!r}\n"
"results = []\n"
"if os.path.isdir(base):\n"
" for name in sorted(os.listdir(base)):\n"
" path = os.path.join(base, name)\n"
" if not os.path.isdir(path):\n"
" continue\n"
" units_path = os.path.join(path, \"daylily-omics-analysis\", \"config\", \"units.tsv\")\n"
" count = 0\n"
" try:\n"
" with open(units_path, \"r\", encoding=\"utf-8\") as units_file:\n"
" lines = [line for line in units_file if line.strip()]\n"
" count = max(len(lines) - 1, 0) if lines else 0\n"
" except FileNotFoundError:\n"
" count = 0\n"
" except Exception:\n"
" count = 0\n"
" results.append({\"name\": name, \"unitsCount\": count})\n"
"print(json.dumps(results))\n"
"PY"
)

worksets = ssm_command(region, instance_id, user, command)
if isinstance(worksets, tuple):
return worksets

output = worksets.strip()
if not output:
return {"worksets": []}

try:
parsed = json.loads(output)
except json.JSONDecodeError:
logger.error("Unable to parse analysis worksets output: %s", output)
return {"message": "Unable to retrieve analysis worksets."}, 500

return {"worksets": parsed}


def cancel_job():
user = request.args.get("user", "ec2-user")
instance_id = request.args.get("instance_id")
Expand Down
9 changes: 9 additions & 0 deletions api/validation/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ class QueueStatusSchema(Schema):
QueueStatus = QueueStatusSchema(unknown=INCLUDE)


class AnalysisWorksetsSchema(Schema):
user = fields.String(validate=validate.Length(max=64))
instance_id = fields.String(required=True, validate=validate.Length(max=60))
region = fields.String(required=True, validate=aws_region_validator)


AnalysisWorksets = AnalysisWorksetsSchema(unknown=INCLUDE)


class ScontrolJobSchema(Schema):
user = fields.String(validate=validate.Length(max=64))
instance_id = fields.String(required=True, validate=validate.Length(max=60))
Expand Down
26 changes: 24 additions & 2 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from api.PclusterApiHandler import (
authenticated,
cancel_job,
analysis_worksets,
create_user,
delete_user,
ec2_action,
Expand Down Expand Up @@ -45,8 +46,23 @@
from api.security.csrf.csrf import csrf_needed
from api.security.fingerprint import CognitoFingerprintGenerator
from api.validation import validated, EC2Action
from api.validation.schemas import CreateUser, DeleteUser, GetClusterConfig, GetCustomImageConfig, GetAwsConfig, GetInstanceTypes,\
Login, PushLog, PriceEstimate, GetDcvSession, QueueStatus, ScontrolJob, CancelJob, Sacct
from api.validation.schemas import (
CreateUser,
Comment on lines 48 to +50

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Fix unexpected indent in app imports

The new AnalysisWorksets schema import was added with leading spaces so it is indented beneath the preceding statement. Python treats this as an IndentationError at module load (python -m py_compile app.py fails with “unexpected indent (app.py, line 49)”), preventing the Flask app from starting and making the whole API unusable. Remove the indentation so the import sits at the top level like the other imports.

Useful? React with 👍 / 👎.

DeleteUser,
GetClusterConfig,
GetCustomImageConfig,
GetAwsConfig,
GetInstanceTypes,
Login,
PushLog,
PriceEstimate,
GetDcvSession,
QueueStatus,
ScontrolJob,
CancelJob,
Sacct,
AnalysisWorksets,
)

ADMINS_GROUP = { "admin" }

Expand Down Expand Up @@ -164,6 +180,12 @@ def delete_user_():
def queue_status_():
return queue_status()

@app.route("/manager/analysis_worksets")
@authenticated(ADMINS_GROUP)
@validated(params=AnalysisWorksets)
def analysis_worksets_():
return analysis_worksets()

@app.route("/manager/cancel_job")
@authenticated(ADMINS_GROUP)
@validated(params=CancelJob)
Expand Down
31 changes: 31 additions & 0 deletions frontend/locales/en/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,42 @@
"details": "Details",
"instances": "Instances",
"storage": "Storage",
"analysisWorksets": "Analysis worksets",
"scheduling": "Job status",
"accounting": "Accounting",
"stackEvents": "Stack events",
"costMonitoring": "Cost monitoring"
},
"analysisWorksets": {
"title": "Analysis worksets",
"loadingText": "Loading analysis worksets...",
"columns": {
"name": "Workset",
"units": "Unit count"
},
"actions": {
"refresh": "Refresh"
},
"filtering": {
"placeholder": "Find worksets",
"ariaLabel": "Find analysis worksets",
"countText": "Results: {{count}}",
"empty": {
"title": "No analysis worksets",
"subtitle": "No analysis worksets to display."
},
"noMatch": {
"title": "No matches",
"subtitle": "No analysis worksets match the filters.",
"clearFilter": "Clear filter"
}
},
"empty": {
"title": "No analysis worksets",
"subtitle": "No analysis worksets were found in the analysis results directory.",
"noHeadNodeSubtitle": "The head node must be running before analysis worksets can be retrieved."
}
},
"properties": {
"title": "Properties",
"sshcommand": {
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,39 @@ function QueueStatus(
})
}

async function GetAnalysisWorksets(
clusterName: string,
instanceId: string,
user?: string,
) {
const region =
getState(['app', 'selectedRegion']) || getState(['aws', 'region'])
let url = `manager/analysis_worksets?instance_id=${instanceId}&region=${region}`
if (user) {
url += `&user=${encodeURIComponent(user)}`
}

try {
const response = await request('get', url)
if (response.status === 200) {
setState(
['clusters', 'index', clusterName, 'analysisWorksets'],
response.data.worksets || [],
)
}
} catch (error) {
const axiosError = error as AxiosError
if (axiosError.response) {
console.log(axiosError.response)
const message =
(axiosError.response.data as {message?: string})?.message ||
axiosError.message
notify(`Error: ${message}`, 'error')
}
console.log(error)
}
}

function CancelJob(
instanceId: any,
user: any,
Expand Down Expand Up @@ -986,6 +1019,7 @@ export {
LoadAwsConfig,
GetDcvSession,
QueueStatus,
GetAnalysisWorksets,
CancelJob,
SlurmAccounting,
JobInfo,
Expand Down
Loading