From f7fef278f62792552686f3595d17f407f901366f Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 10 Oct 2026 10:29:02 +0000 Subject: [PATCH] docs(apiserver): fix swag annotations and regenerate the swagger spec (bug-1966) Cause: the handler annotations referenced types that do not exist (models.Error, ErrorResponse, JobsResponse, JobSignalRequest, JobControlResponseSummary, unqualified JobSubmitRequest), had malformed @Param/@Failure lines and /api/v1 paths, and there was no general API info, so `swag init -g cmd/main.go` failed and docs/ stayed a stale stub. Fix: use the real response envelopes (JsonStruct / JsonErrStruct) and request models, the real routes under @BasePath /xce/v1, add general info to cmd/main.go, and regenerate docs/ with `swag init -g cmd/main.go -o docs`; the spec now has /jobs/{jobids}/control and /jobs/control. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/frontend/apiserver/cmd/main.go | 8 + src/frontend/apiserver/controllers/hosts.go | 6 +- src/frontend/apiserver/controllers/jobs.go | 53 +- src/frontend/apiserver/docs/docs.go | 868 ++++++++++++++------ src/frontend/apiserver/docs/swagger.json | 852 +++++++++++++------ src/frontend/apiserver/docs/swagger.yaml | 597 ++++++++++---- 6 files changed, 1700 insertions(+), 684 deletions(-) diff --git a/src/frontend/apiserver/cmd/main.go b/src/frontend/apiserver/cmd/main.go index fcb3140b..f85b8998 100644 --- a/src/frontend/apiserver/cmd/main.go +++ b/src/frontend/apiserver/cmd/main.go @@ -64,6 +64,14 @@ func composeVersionDisplay(gitCommit, dirtyFingerprint string) string { return strings.TrimSuffix(gitCommit, "-dirty") + "-" + dirtyFingerprint } +// General API info for swag; regenerate docs/ from src/frontend/apiserver +// with `swag init -g cmd/main.go -o docs`. +// +// @title XCE API Server +// @version 1.0 +// @description XCE cluster management REST API. +// @host localhost:8443 +// @BasePath /xce/v1 func main() { gin.SetMode(gin.ReleaseMode) gin.DefaultWriter = io.Discard diff --git a/src/frontend/apiserver/controllers/hosts.go b/src/frontend/apiserver/controllers/hosts.go index a15975ab..7fb1d58b 100644 --- a/src/frontend/apiserver/controllers/hosts.go +++ b/src/frontend/apiserver/controllers/hosts.go @@ -26,9 +26,9 @@ type HostHandler struct { // @Tags hosts // @Accept json // @Produce json -// @Success 200 -// @Failure 400 {object} models.Error -// @Router /api/v1/hosts [get] +// @Success 200 {object} JsonStruct +// @Failure 400 {object} JsonErrStruct +// @Router /hosts [get] func (h *HostHandler) GetHostsInfo(ctx *gin.Context) { logger.Debug("Enter GetAllHosts() function from ", zap.String("client_ip", ctx.ClientIP())) diff --git a/src/frontend/apiserver/controllers/jobs.go b/src/frontend/apiserver/controllers/jobs.go index 0d2da44f..ac4205cc 100644 --- a/src/frontend/apiserver/controllers/jobs.go +++ b/src/frontend/apiserver/controllers/jobs.go @@ -49,11 +49,11 @@ type JobController struct{} // @Tags jobs // @Accept json // @Produce json -// @Param filter query string "filter=[{user:eq:user1},{queue:eq:queue1}]" -// @Param fields query string "fields=jobid,status,queue" -// @Success 200 {array} models.JobInfo "Response for jobs info" -// @Failure 400 {object} models.Error -// @Router /api/v1/jobs [get] +// @Param filter query string false "filter, e.g. user:eq:user1,queue:eq:queue1" +// @Param fields query string false "fields, e.g. jobid,status,queue" +// @Success 200 {object} JsonStruct "Response for jobs info" +// @Failure 400 {object} JsonErrStruct +// @Router /jobs [get] func (j JobController) GetJobsInfo(ctx *gin.Context) { logger.Debug("Enter GetJobsInfo() function from ", zap.String("client_ip", ctx.ClientIP())) var fields []string @@ -545,11 +545,9 @@ func returnSelectionError(ctx *gin.Context, raw string, err error) { // @Produce json // @Param jobids path string true "Comma-separated list of job IDs (e.g., 1001,1002,1003)" // @Param fields query string false "Comma-separated list of fields to return (e.g., jobid,status,queue)" -// @Success 200 {object} JobsResponse "Successfully retrieved jobs" -// @Failure 400 INVALID_PARAMS_CODE "invalid parameters" -// @Failure 500 ERROR_MSG -// @Example {curl} Example Usage: -// @Example curl -X GET "http://localhost:8080/jobs/1001,1002,1003?fields=jobid,status,queue" +// @Success 200 {object} JsonStruct "Successfully retrieved jobs" +// @Failure 400 {object} JsonErrStruct "invalid parameters" +// @Failure 500 {object} JsonErrStruct "Internal server error" // @Router /jobs/{jobids} [get] func (j JobController) GetJobByIDs(ctx *gin.Context) { logger.Debug("Enter GetJobByIDs() function from ", zap.String("client_ip", ctx.ClientIP())) @@ -1087,13 +1085,13 @@ func (j JobController) RestartJobByID(ctx *gin.Context) { // @Tags jobs // @Accept json // @Produce json -// @Param job body JobSubmitRequest true "Job submission parameters" -// @Success 201 {object} JobSubmitResponse "Job successfully created" -// @Failure 400 {object} ErrorResponse "Invalid request body or parameters" -// @Failure 401 {object} ErrorResponse "Unauthorized - Invalid or missing credentials" -// @Failure 403 {object} ErrorResponse "Forbidden - User lacks permission" -// @Failure 500 {object} ErrorResponse "Internal server error" -// @Router /jobs [post] +// @Param job body models.JobSubmitRequest true "Job submission parameters" +// @Success 201 {object} JsonStruct "Job successfully created" +// @Failure 400 {object} JsonErrStruct "Invalid request body or parameters" +// @Failure 401 {object} JsonErrStruct "Unauthorized - Invalid or missing credentials" +// @Failure 403 {object} JsonErrStruct "Forbidden - User lacks permission" +// @Failure 500 {object} JsonErrStruct "Internal server error" +// @Router /jobs/create [post] func (j JobController) CreateJob(ctx *gin.Context) { logger.Debug("Enter CreateJob() function from ", zap.String("client_ip", ctx.ClientIP())) @@ -2046,11 +2044,11 @@ func postJobRequest(job *models.JobInfo) error { // @Accept json // @Produce json // @Param jobids path string true "Job ID" -// @Param action body JobSignalRequest true "Action to be performed" -// @Success 202 {object} JobControlResponseSummary "Action successfully accepted" -// @Failure 400 {object} ErrorResponse "Invalid request body or parameters" -// @Failure 404 {object} ErrorResponse "Job not found" -// @Failure 500 {object} ErrorResponse "Internal server error" +// @Param action body models.JobControlJobIDRequest true "Action to be performed" +// @Success 202 {object} JsonStruct "Action successfully accepted" +// @Failure 400 {object} JsonErrStruct "Invalid request body or parameters" +// @Failure 404 {object} JsonErrStruct "Job not found" +// @Failure 500 {object} JsonErrStruct "Internal server error" // @Router /jobs/{jobids}/control [post] func (j JobController) ControlJobByID(ctx *gin.Context) { logger.Debug("Enter ControlJobByID() function from ", zap.String("client_ip", ctx.ClientIP())) @@ -2322,12 +2320,11 @@ func (j JobController) ControlJobByID(ctx *gin.Context) { // @Tags jobs // @Accept json // @Produce json -// @Param jobids path string true "Job ID" -// @Param action body JobControlRequest true "Action to be performed" -// @Success 202 {object} JobControlResponseSummary "Action successfully accepted" -// @Failure 400 {object} ErrorResponse "Invalid request body or parameters" -// @Failure 404 {object} ErrorResponse "Job not found" -// @Failure 500 {object} ErrorResponse "Internal server error" +// @Param action body models.JobControlRequest true "Action to be performed" +// @Success 202 {object} JsonStruct "Action successfully accepted" +// @Failure 400 {object} JsonErrStruct "Invalid request body or parameters" +// @Failure 404 {object} JsonErrStruct "Job not found" +// @Failure 500 {object} JsonErrStruct "Internal server error" // @Router /jobs/control [post] func (j JobController) ControlJob(ctx *gin.Context) { logger.Debug("Enter ControlJob() function from ", zap.String("client_ip", ctx.ClientIP())) diff --git a/src/frontend/apiserver/docs/docs.go b/src/frontend/apiserver/docs/docs.go index 1a793dd1..5b3b9524 100644 --- a/src/frontend/apiserver/docs/docs.go +++ b/src/frontend/apiserver/docs/docs.go @@ -15,9 +15,9 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/api/v1/hosts": { + "/hosts": { "get": { - "description": "Get basic or full host information", + "description": "Get host information", "consumes": [ "application/json" ], @@ -28,29 +28,279 @@ const docTemplate = `{ "hosts" ], "summary": "Get host information", - "parameters": [ - { - "type": "string", - "description": "Host type (basic or full)", - "name": "type", - "in": "query", - "required": true - } - ], "responses": { "200": { - "description": "Response for full host type", + "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.FullHost" - } + "$ref": "#/definitions/controllers.JsonStruct" } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/models.Error" + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs": { + "get": { + "description": "Get jobs information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Get jobs information", + "parameters": [ + { + "type": "string", + "description": "filter, e.g. user:eq:user1,queue:eq:queue1", + "name": "filter", + "in": "query" + }, + { + "type": "string", + "description": "fields, e.g. jobid,status,queue", + "name": "fields", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Response for jobs info", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/control": { + "post": { + "description": "Submit an action to be performed on specific jobs identified by filter in request body.\nkill/stop/resume signals as for POST /jobs/{jobids}/control.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Perform an action on filtered out jobs", + "parameters": [ + { + "description": "Action to be performed", + "name": "action", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.JobControlRequest" + } + } + ], + "responses": { + "202": { + "description": "Action successfully accepted", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Invalid request body or parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "404": { + "description": "Job not found", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/create": { + "post": { + "description": "Submit a new job with specified requirements and parameters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Create a new job", + "parameters": [ + { + "description": "Job submission parameters", + "name": "job", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.JobSubmitRequest" + } + } + ], + "responses": { + "201": { + "description": "Job successfully created", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Invalid request body or parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "401": { + "description": "Unauthorized - Invalid or missing credentials", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "403": { + "description": "Forbidden - User lacks permission", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/{jobids}": { + "get": { + "description": "Retrieve information for multiple jobs in a single request with optional field selection", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Get multiple jobs by their IDs", + "parameters": [ + { + "type": "string", + "description": "Comma-separated list of job IDs (e.g., 1001,1002,1003)", + "name": "jobids", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Comma-separated list of fields to return (e.g., jobid,status,queue)", + "name": "fields", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved jobs", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "invalid parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/{jobids}/control": { + "post": { + "description": "Submit an action to be performed on a specific job identified by jobids.\nkill without a signal: SIGINT, SIGTERM, then SIGKILL, job.terminate_interval\nseconds apart while the job runs; with a signal: that signal once; with\nforce_kill: SIGKILL at once. stop: SIGTSTP, then SIGSTOP after the interval\nif the job has not stopped. resume: SIGCONT (cancels a stop in progress).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Perform an action on a job", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "jobids", + "in": "path", + "required": true + }, + { + "description": "Action to be performed", + "name": "action", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.JobControlJobIDRequest" + } + } + ], + "responses": { + "202": { + "description": "Action successfully accepted", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Invalid request body or parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "404": { + "description": "Job not found", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" } } } @@ -58,267 +308,395 @@ const docTemplate = `{ } }, "definitions": { - "models.BasicHost": { + "controllers.JsonErrStruct": { "type": "object", "properties": { - "DResources": { - "type": "array", - "items": { - "type": "string" - } - }, - "busyThreshold": { - "type": "array", - "items": { - "type": "number" - } - }, - "cores": { + "code": { "type": "integer" }, - "cores_per_proc": { + "details": {}, + "msg": {} + } + }, + "controllers.JsonStruct": { + "type": "object", + "properties": { + "code": { "type": "integer" }, - "cpuFactor": { - "type": "number" - }, - "hostAddr": { - "type": "string" - }, - "hostModel": { - "type": "string" - }, - "hostName": { - "type": "string" - }, - "hostType": { - "type": "string" - }, - "isServer": { - "type": "boolean" - }, - "maxCpus": { + "data": {}, + "msg": {} + } + }, + "models.CustomInt": { + "type": "object", + "properties": { + "seconds": { "type": "integer" - }, - "maxMem": { - "type": "integer" - }, - "maxSwap": { - "type": "integer" - }, - "maxTmp": { - "type": "integer" - }, - "nDRes": { - "type": "integer" - }, - "nDisks": { - "type": "integer" - }, - "nRes": { - "type": "integer" - }, - "numIndx": { - "type": "integer" - }, - "pprocs": { - "type": "integer" - }, - "resources": { - "type": "array", - "items": { - "type": "string" - } - }, - "threads_per_core": { - "type": "integer" - }, - "windows": { + } + } + }, + "models.CustomLimit": { + "type": "object", + "properties": { + "mb": { + "type": "number", + "format": "float64" + } + } + }, + "models.CustomTime": { + "type": "object", + "properties": { + "time": { "type": "string" } } }, - "models.Error": { + "models.JobControlFilter": { "type": "object", "properties": { - "error": { + "all_jobs": { + "type": "boolean" + }, + "exec_hosts": { + "type": "string" + }, + "job_group_name": { + "type": "string" + }, + "job_name": { + "type": "string" + }, + "queue_name": { + "type": "string" + }, + "sla": { + "type": "string" + }, + "users": { "type": "string" } } }, - "models.FullHost": { + "models.JobControlJobIDRequest": { "type": "object", + "required": [ + "action" + ], "properties": { - "DResources": { - "type": "array", - "items": { - "type": "string" - } - }, - "affinityCpulist": { + "action": { "type": "string" }, - "attr": { - "type": "integer" - }, - "busySched": { - "type": "number" - }, - "busyStop": { - "type": "number" - }, - "busyThreshold": { - "type": "array", - "items": { - "type": "number" - } - }, - "chkSig": { - "type": "integer" - }, - "cnsmrAvail": { - "type": "number" - }, - "cnsmrUsage": { - "type": "number" - }, - "cores": { - "type": "integer" - }, - "cores_per_proc": { - "type": "integer" - }, - "cpuFactor": { - "type": "number" - }, - "cycleTimeLeft": { - "type": "integer" - }, - "hCtrlMsg": { - "type": "integer" - }, - "hStatus": { + "comment": { "type": "string" }, - "hostAddr": { - "type": "string" - }, - "hostModel": { - "type": "string" - }, - "hostName": { - "type": "string" - }, - "hostType": { - "type": "string" - }, - "idleTime": { - "type": "integer" - }, - "isServer": { + "force_kill": { + "description": "kill: SIGKILL at once (no graceful sequence)", "type": "boolean" }, - "load": { - "type": "array", - "items": { - "type": "number" - } + "method": { + "description": "\"criu\" as default", + "type": "string" }, - "loadSched": { - "type": "array", - "items": { - "type": "number" - } + "signal": { + "description": "kill only: send this signal once instead of the graceful sequence", + "type": "string" + } + } + }, + "models.JobControlRequest": { + "type": "object", + "required": [ + "action", + "filter" + ], + "properties": { + "action": { + "type": "string" }, - "loadStop": { - "type": "array", - "items": { - "type": "number" - } + "comment": { + "type": "string" }, - "maxAvail": { - "type": "number" + "filter": { + "$ref": "#/definitions/models.JobControlFilter" }, - "maxCpus": { + "force_kill": { + "description": "kill: SIGKILL at once (no graceful sequence)", + "type": "boolean" + }, + "method": { + "description": "\"criu\" as default", + "type": "string" + }, + "signal": { + "description": "kill only: send this signal once instead of the graceful sequence", + "type": "string" + } + } + }, + "models.JobSubmitRequest": { + "type": "object", + "required": [ + "command" + ], + "properties": { + "affinity": { + "type": "string" + }, + "asked_hosts": { + "type": "string" + }, + "begin_time": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomTime" + } + ] + }, + "chkpnt_dir": { + "type": "string" + }, + "chkpnt_period": { "type": "integer" }, - "maxExitRate": { - "type": "integer" + "command": { + "type": "string" }, - "maxJobs": { - "type": "integer" + "core_limit": { + "$ref": "#/definitions/models.CustomLimit" }, - "maxMem": { - "type": "integer" + "cpu_limit": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomInt" + } + ] }, - "maxSwap": { - "type": "integer" + "cwd": { + "type": "string" }, - "maxTmp": { - "type": "integer" + "data_limit": { + "$ref": "#/definitions/models.CustomLimit" }, - "mig": { - "type": "integer" + "data_spec_file": { + "type": "string" }, - "nDRes": { - "type": "integer" + "depend_cond": { + "type": "string" }, - "nDisks": { - "type": "integer" - }, - "nIdx": { - "type": "integer" - }, - "nRes": { - "type": "integer" - }, - "numExitRate": { - "type": "integer" - }, - "numIndx": { - "type": "integer" - }, - "numJobs": { - "type": "integer" - }, - "numRESERVE": { - "type": "integer" - }, - "numRUN": { - "type": "integer" - }, - "numSSUSP": { - "type": "integer" - }, - "numUSUSP": { - "type": "integer" - }, - "pprocs": { - "type": "integer" - }, - "prvdrAvail": { - "type": "number" - }, - "prvdrUsage": { - "type": "number" - }, - "realLoad": { - "type": "number" - }, - "resources": { + "env": { "type": "array", "items": { "type": "string" } }, - "threads_per_core": { - "type": "integer" - }, - "userJobLimit": { - "type": "integer" - }, - "windows": { + "err_file": { "type": "string" + }, + "except_list": { + "type": "string" + }, + "exclusive": { + "type": "boolean" + }, + "extend_data": { + "type": "string" + }, + "file_limit": { + "description": "fsize", + "allOf": [ + { + "$ref": "#/definitions/models.CustomLimit" + } + ] + }, + "from_host": { + "description": "client-asserted submitting hostname (bug-1676) -- passed through unchanged, not derived server-side", + "type": "string" + }, + "gpureq": { + "type": "string" + }, + "host_spec": { + "type": "string" + }, + "in_file": { + "type": "string" + }, + "init_chkpnt_period": { + "type": "integer" + }, + "interactive": { + "type": "integer" + }, + "job_description": { + "type": "string" + }, + "job_group": { + "type": "string" + }, + "job_name": { + "type": "string" + }, + "job_priority": { + "type": "integer" + }, + "launch_type": { + "type": "string" + }, + "license_project": { + "type": "string" + }, + "login_shell": { + "type": "string" + }, + "mailuser": { + "type": "string" + }, + "maxnum_processors": { + "type": "integer" + }, + "mem_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "mig_threshold": { + "type": "integer" + }, + "num_files": { + "type": "integer" + }, + "num_processors": { + "type": "integer" + }, + "out_dir": { + "type": "string" + }, + "out_file": { + "type": "string" + }, + "post_exec_cmd": { + "type": "string" + }, + "pre_exec_cmd": { + "type": "string" + }, + "process_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "project_name": { + "description": "default project: \"default\"", + "type": "string" + }, + "queue_name": { + "description": "set to DEFAULT_QUEUE in config", + "type": "string" + }, + "requeue_error_values": { + "type": "string" + }, + "rerunable": { + "type": "boolean" + }, + "resreq": { + "description": "default: \"type == local\"", + "type": "string" + }, + "rsv_id": { + "type": "string" + }, + "run_limit": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomInt" + } + ] + }, + "run_time_estimation": { + "type": "integer" + }, + "shell_limit": { + "$ref": "#/definitions/models.ShellLimit" + }, + "sla": { + "type": "string" + }, + "stack_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "swap_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "term_time": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomTime" + } + ] + }, + "thread_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "transfer_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_group": { + "type": "string" + }, + "warning_action": { + "type": "string" + }, + "warning_time_period": { + "type": "integer" + } + } + }, + "models.ShellLimit": { + "type": "object", + "properties": { + "core_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "cpu_limit": { + "description": "in milliseconds", + "type": "integer" + }, + "data_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "file_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "mem_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "num_files": { + "type": "integer" + }, + "process_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "stack_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "swap_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "thread_limit": { + "$ref": "#/definitions/models.CustomLimit" } } } @@ -327,18 +705,18 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: "1.0", - Host: "localhost:8443", - BasePath: "/", - Schemes: []string{}, - Title: "Host Management API", - Description: "API Server", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", + Version: "1.0", + Host: "localhost:8443", + BasePath: "/xce/v1", + Schemes: []string{}, + Title: "XCE API Server", + Description: "XCE cluster management REST API.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", } func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) } diff --git a/src/frontend/apiserver/docs/swagger.json b/src/frontend/apiserver/docs/swagger.json index 034e8e78..0a345fe0 100644 --- a/src/frontend/apiserver/docs/swagger.json +++ b/src/frontend/apiserver/docs/swagger.json @@ -1,17 +1,17 @@ { "swagger": "2.0", "info": { - "description": "API Server", - "title": "Host Management API", + "description": "XCE cluster management REST API.", + "title": "XCE API Server", "contact": {}, "version": "1.0" }, "host": "localhost:8443", - "basePath": "/", + "basePath": "/xce/v1", "paths": { - "/api/v1/hosts": { + "/hosts": { "get": { - "description": "Get basic or full host information", + "description": "Get host information", "consumes": [ "application/json" ], @@ -22,29 +22,279 @@ "hosts" ], "summary": "Get host information", - "parameters": [ - { - "type": "string", - "description": "Host type (basic or full)", - "name": "type", - "in": "query", - "required": true - } - ], "responses": { "200": { - "description": "Response for full host type", + "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.FullHost" - } + "$ref": "#/definitions/controllers.JsonStruct" } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/models.Error" + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs": { + "get": { + "description": "Get jobs information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Get jobs information", + "parameters": [ + { + "type": "string", + "description": "filter, e.g. user:eq:user1,queue:eq:queue1", + "name": "filter", + "in": "query" + }, + { + "type": "string", + "description": "fields, e.g. jobid,status,queue", + "name": "fields", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Response for jobs info", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/control": { + "post": { + "description": "Submit an action to be performed on specific jobs identified by filter in request body.\nkill/stop/resume signals as for POST /jobs/{jobids}/control.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Perform an action on filtered out jobs", + "parameters": [ + { + "description": "Action to be performed", + "name": "action", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.JobControlRequest" + } + } + ], + "responses": { + "202": { + "description": "Action successfully accepted", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Invalid request body or parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "404": { + "description": "Job not found", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/create": { + "post": { + "description": "Submit a new job with specified requirements and parameters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Create a new job", + "parameters": [ + { + "description": "Job submission parameters", + "name": "job", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.JobSubmitRequest" + } + } + ], + "responses": { + "201": { + "description": "Job successfully created", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Invalid request body or parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "401": { + "description": "Unauthorized - Invalid or missing credentials", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "403": { + "description": "Forbidden - User lacks permission", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/{jobids}": { + "get": { + "description": "Retrieve information for multiple jobs in a single request with optional field selection", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Get multiple jobs by their IDs", + "parameters": [ + { + "type": "string", + "description": "Comma-separated list of job IDs (e.g., 1001,1002,1003)", + "name": "jobids", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Comma-separated list of fields to return (e.g., jobid,status,queue)", + "name": "fields", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved jobs", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "invalid parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + } + } + } + }, + "/jobs/{jobids}/control": { + "post": { + "description": "Submit an action to be performed on a specific job identified by jobids.\nkill without a signal: SIGINT, SIGTERM, then SIGKILL, job.terminate_interval\nseconds apart while the job runs; with a signal: that signal once; with\nforce_kill: SIGKILL at once. stop: SIGTSTP, then SIGSTOP after the interval\nif the job has not stopped. resume: SIGCONT (cancels a stop in progress).", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "jobs" + ], + "summary": "Perform an action on a job", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "jobids", + "in": "path", + "required": true + }, + { + "description": "Action to be performed", + "name": "action", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.JobControlJobIDRequest" + } + } + ], + "responses": { + "202": { + "description": "Action successfully accepted", + "schema": { + "$ref": "#/definitions/controllers.JsonStruct" + } + }, + "400": { + "description": "Invalid request body or parameters", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "404": { + "description": "Job not found", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/controllers.JsonErrStruct" } } } @@ -52,267 +302,395 @@ } }, "definitions": { - "models.BasicHost": { + "controllers.JsonErrStruct": { "type": "object", "properties": { - "DResources": { - "type": "array", - "items": { - "type": "string" - } - }, - "busyThreshold": { - "type": "array", - "items": { - "type": "number" - } - }, - "cores": { + "code": { "type": "integer" }, - "cores_per_proc": { + "details": {}, + "msg": {} + } + }, + "controllers.JsonStruct": { + "type": "object", + "properties": { + "code": { "type": "integer" }, - "cpuFactor": { - "type": "number" - }, - "hostAddr": { - "type": "string" - }, - "hostModel": { - "type": "string" - }, - "hostName": { - "type": "string" - }, - "hostType": { - "type": "string" - }, - "isServer": { - "type": "boolean" - }, - "maxCpus": { + "data": {}, + "msg": {} + } + }, + "models.CustomInt": { + "type": "object", + "properties": { + "seconds": { "type": "integer" - }, - "maxMem": { - "type": "integer" - }, - "maxSwap": { - "type": "integer" - }, - "maxTmp": { - "type": "integer" - }, - "nDRes": { - "type": "integer" - }, - "nDisks": { - "type": "integer" - }, - "nRes": { - "type": "integer" - }, - "numIndx": { - "type": "integer" - }, - "pprocs": { - "type": "integer" - }, - "resources": { - "type": "array", - "items": { - "type": "string" - } - }, - "threads_per_core": { - "type": "integer" - }, - "windows": { + } + } + }, + "models.CustomLimit": { + "type": "object", + "properties": { + "mb": { + "type": "number", + "format": "float64" + } + } + }, + "models.CustomTime": { + "type": "object", + "properties": { + "time": { "type": "string" } } }, - "models.Error": { + "models.JobControlFilter": { "type": "object", "properties": { - "error": { + "all_jobs": { + "type": "boolean" + }, + "exec_hosts": { + "type": "string" + }, + "job_group_name": { + "type": "string" + }, + "job_name": { + "type": "string" + }, + "queue_name": { + "type": "string" + }, + "sla": { + "type": "string" + }, + "users": { "type": "string" } } }, - "models.FullHost": { + "models.JobControlJobIDRequest": { "type": "object", + "required": [ + "action" + ], "properties": { - "DResources": { - "type": "array", - "items": { - "type": "string" - } - }, - "affinityCpulist": { + "action": { "type": "string" }, - "attr": { - "type": "integer" - }, - "busySched": { - "type": "number" - }, - "busyStop": { - "type": "number" - }, - "busyThreshold": { - "type": "array", - "items": { - "type": "number" - } - }, - "chkSig": { - "type": "integer" - }, - "cnsmrAvail": { - "type": "number" - }, - "cnsmrUsage": { - "type": "number" - }, - "cores": { - "type": "integer" - }, - "cores_per_proc": { - "type": "integer" - }, - "cpuFactor": { - "type": "number" - }, - "cycleTimeLeft": { - "type": "integer" - }, - "hCtrlMsg": { - "type": "integer" - }, - "hStatus": { + "comment": { "type": "string" }, - "hostAddr": { - "type": "string" - }, - "hostModel": { - "type": "string" - }, - "hostName": { - "type": "string" - }, - "hostType": { - "type": "string" - }, - "idleTime": { - "type": "integer" - }, - "isServer": { + "force_kill": { + "description": "kill: SIGKILL at once (no graceful sequence)", "type": "boolean" }, - "load": { - "type": "array", - "items": { - "type": "number" - } + "method": { + "description": "\"criu\" as default", + "type": "string" }, - "loadSched": { - "type": "array", - "items": { - "type": "number" - } + "signal": { + "description": "kill only: send this signal once instead of the graceful sequence", + "type": "string" + } + } + }, + "models.JobControlRequest": { + "type": "object", + "required": [ + "action", + "filter" + ], + "properties": { + "action": { + "type": "string" }, - "loadStop": { - "type": "array", - "items": { - "type": "number" - } + "comment": { + "type": "string" }, - "maxAvail": { - "type": "number" + "filter": { + "$ref": "#/definitions/models.JobControlFilter" }, - "maxCpus": { + "force_kill": { + "description": "kill: SIGKILL at once (no graceful sequence)", + "type": "boolean" + }, + "method": { + "description": "\"criu\" as default", + "type": "string" + }, + "signal": { + "description": "kill only: send this signal once instead of the graceful sequence", + "type": "string" + } + } + }, + "models.JobSubmitRequest": { + "type": "object", + "required": [ + "command" + ], + "properties": { + "affinity": { + "type": "string" + }, + "asked_hosts": { + "type": "string" + }, + "begin_time": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomTime" + } + ] + }, + "chkpnt_dir": { + "type": "string" + }, + "chkpnt_period": { "type": "integer" }, - "maxExitRate": { - "type": "integer" + "command": { + "type": "string" }, - "maxJobs": { - "type": "integer" + "core_limit": { + "$ref": "#/definitions/models.CustomLimit" }, - "maxMem": { - "type": "integer" + "cpu_limit": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomInt" + } + ] }, - "maxSwap": { - "type": "integer" + "cwd": { + "type": "string" }, - "maxTmp": { - "type": "integer" + "data_limit": { + "$ref": "#/definitions/models.CustomLimit" }, - "mig": { - "type": "integer" + "data_spec_file": { + "type": "string" }, - "nDRes": { - "type": "integer" + "depend_cond": { + "type": "string" }, - "nDisks": { - "type": "integer" - }, - "nIdx": { - "type": "integer" - }, - "nRes": { - "type": "integer" - }, - "numExitRate": { - "type": "integer" - }, - "numIndx": { - "type": "integer" - }, - "numJobs": { - "type": "integer" - }, - "numRESERVE": { - "type": "integer" - }, - "numRUN": { - "type": "integer" - }, - "numSSUSP": { - "type": "integer" - }, - "numUSUSP": { - "type": "integer" - }, - "pprocs": { - "type": "integer" - }, - "prvdrAvail": { - "type": "number" - }, - "prvdrUsage": { - "type": "number" - }, - "realLoad": { - "type": "number" - }, - "resources": { + "env": { "type": "array", "items": { "type": "string" } }, - "threads_per_core": { - "type": "integer" - }, - "userJobLimit": { - "type": "integer" - }, - "windows": { + "err_file": { "type": "string" + }, + "except_list": { + "type": "string" + }, + "exclusive": { + "type": "boolean" + }, + "extend_data": { + "type": "string" + }, + "file_limit": { + "description": "fsize", + "allOf": [ + { + "$ref": "#/definitions/models.CustomLimit" + } + ] + }, + "from_host": { + "description": "client-asserted submitting hostname (bug-1676) -- passed through unchanged, not derived server-side", + "type": "string" + }, + "gpureq": { + "type": "string" + }, + "host_spec": { + "type": "string" + }, + "in_file": { + "type": "string" + }, + "init_chkpnt_period": { + "type": "integer" + }, + "interactive": { + "type": "integer" + }, + "job_description": { + "type": "string" + }, + "job_group": { + "type": "string" + }, + "job_name": { + "type": "string" + }, + "job_priority": { + "type": "integer" + }, + "launch_type": { + "type": "string" + }, + "license_project": { + "type": "string" + }, + "login_shell": { + "type": "string" + }, + "mailuser": { + "type": "string" + }, + "maxnum_processors": { + "type": "integer" + }, + "mem_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "mig_threshold": { + "type": "integer" + }, + "num_files": { + "type": "integer" + }, + "num_processors": { + "type": "integer" + }, + "out_dir": { + "type": "string" + }, + "out_file": { + "type": "string" + }, + "post_exec_cmd": { + "type": "string" + }, + "pre_exec_cmd": { + "type": "string" + }, + "process_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "project_name": { + "description": "default project: \"default\"", + "type": "string" + }, + "queue_name": { + "description": "set to DEFAULT_QUEUE in config", + "type": "string" + }, + "requeue_error_values": { + "type": "string" + }, + "rerunable": { + "type": "boolean" + }, + "resreq": { + "description": "default: \"type == local\"", + "type": "string" + }, + "rsv_id": { + "type": "string" + }, + "run_limit": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomInt" + } + ] + }, + "run_time_estimation": { + "type": "integer" + }, + "shell_limit": { + "$ref": "#/definitions/models.ShellLimit" + }, + "sla": { + "type": "string" + }, + "stack_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "swap_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "term_time": { + "description": "k change type", + "allOf": [ + { + "$ref": "#/definitions/models.CustomTime" + } + ] + }, + "thread_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "transfer_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_group": { + "type": "string" + }, + "warning_action": { + "type": "string" + }, + "warning_time_period": { + "type": "integer" + } + } + }, + "models.ShellLimit": { + "type": "object", + "properties": { + "core_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "cpu_limit": { + "description": "in milliseconds", + "type": "integer" + }, + "data_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "file_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "mem_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "num_files": { + "type": "integer" + }, + "process_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "stack_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "swap_limit": { + "$ref": "#/definitions/models.CustomLimit" + }, + "thread_limit": { + "$ref": "#/definitions/models.CustomLimit" } } } diff --git a/src/frontend/apiserver/docs/swagger.yaml b/src/frontend/apiserver/docs/swagger.yaml index 70596bd2..e5c8ead4 100644 --- a/src/frontend/apiserver/docs/swagger.yaml +++ b/src/frontend/apiserver/docs/swagger.yaml @@ -1,212 +1,467 @@ -basePath: / +basePath: /xce/v1 definitions: - models.BasicHost: + controllers.JsonErrStruct: properties: - DResources: - items: - type: string - type: array - busyThreshold: - items: - type: number - type: array - cores: + code: type: integer - cores_per_proc: + details: {} + msg: {} + type: object + controllers.JsonStruct: + properties: + code: type: integer - cpuFactor: + data: {} + msg: {} + type: object + models.CustomInt: + properties: + seconds: + type: integer + type: object + models.CustomLimit: + properties: + mb: + format: float64 type: number - hostAddr: - type: string - hostModel: - type: string - hostName: - type: string - hostType: - type: string - isServer: - type: boolean - maxCpus: - type: integer - maxMem: - type: integer - maxSwap: - type: integer - maxTmp: - type: integer - nDRes: - type: integer - nDisks: - type: integer - nRes: - type: integer - numIndx: - type: integer - pprocs: - type: integer - resources: - items: - type: string - type: array - threads_per_core: - type: integer - windows: + type: object + models.CustomTime: + properties: + time: type: string type: object - models.Error: + models.JobControlFilter: properties: - error: + all_jobs: + type: boolean + exec_hosts: + type: string + job_group_name: + type: string + job_name: + type: string + queue_name: + type: string + sla: + type: string + users: type: string type: object - models.FullHost: + models.JobControlJobIDRequest: properties: - DResources: - items: - type: string - type: array - affinityCpulist: + action: type: string - attr: - type: integer - busySched: - type: number - busyStop: - type: number - busyThreshold: - items: - type: number - type: array - chkSig: - type: integer - cnsmrAvail: - type: number - cnsmrUsage: - type: number - cores: - type: integer - cores_per_proc: - type: integer - cpuFactor: - type: number - cycleTimeLeft: - type: integer - hCtrlMsg: - type: integer - hStatus: + comment: type: string - hostAddr: - type: string - hostModel: - type: string - hostName: - type: string - hostType: - type: string - idleTime: - type: integer - isServer: + force_kill: + description: 'kill: SIGKILL at once (no graceful sequence)' type: boolean - load: - items: - type: number - type: array - loadSched: - items: - type: number - type: array - loadStop: - items: - type: number - type: array - maxAvail: - type: number - maxCpus: + method: + description: '"criu" as default' + type: string + signal: + description: 'kill only: send this signal once instead of the graceful sequence' + type: string + required: + - action + type: object + models.JobControlRequest: + properties: + action: + type: string + comment: + type: string + filter: + $ref: '#/definitions/models.JobControlFilter' + force_kill: + description: 'kill: SIGKILL at once (no graceful sequence)' + type: boolean + method: + description: '"criu" as default' + type: string + signal: + description: 'kill only: send this signal once instead of the graceful sequence' + type: string + required: + - action + - filter + type: object + models.JobSubmitRequest: + properties: + affinity: + type: string + asked_hosts: + type: string + begin_time: + allOf: + - $ref: '#/definitions/models.CustomTime' + description: k change type + chkpnt_dir: + type: string + chkpnt_period: type: integer - maxExitRate: - type: integer - maxJobs: - type: integer - maxMem: - type: integer - maxSwap: - type: integer - maxTmp: - type: integer - mig: - type: integer - nDRes: - type: integer - nDisks: - type: integer - nIdx: - type: integer - nRes: - type: integer - numExitRate: - type: integer - numIndx: - type: integer - numJobs: - type: integer - numRESERVE: - type: integer - numRUN: - type: integer - numSSUSP: - type: integer - numUSUSP: - type: integer - pprocs: - type: integer - prvdrAvail: - type: number - prvdrUsage: - type: number - realLoad: - type: number - resources: + command: + type: string + core_limit: + $ref: '#/definitions/models.CustomLimit' + cpu_limit: + allOf: + - $ref: '#/definitions/models.CustomInt' + description: k change type + cwd: + type: string + data_limit: + $ref: '#/definitions/models.CustomLimit' + data_spec_file: + type: string + depend_cond: + type: string + env: items: type: string type: array - threads_per_core: - type: integer - userJobLimit: - type: integer - windows: + err_file: type: string + except_list: + type: string + exclusive: + type: boolean + extend_data: + type: string + file_limit: + allOf: + - $ref: '#/definitions/models.CustomLimit' + description: fsize + from_host: + description: client-asserted submitting hostname (bug-1676) -- passed through + unchanged, not derived server-side + type: string + gpureq: + type: string + host_spec: + type: string + in_file: + type: string + init_chkpnt_period: + type: integer + interactive: + type: integer + job_description: + type: string + job_group: + type: string + job_name: + type: string + job_priority: + type: integer + launch_type: + type: string + license_project: + type: string + login_shell: + type: string + mailuser: + type: string + maxnum_processors: + type: integer + mem_limit: + $ref: '#/definitions/models.CustomLimit' + mig_threshold: + type: integer + num_files: + type: integer + num_processors: + type: integer + out_dir: + type: string + out_file: + type: string + post_exec_cmd: + type: string + pre_exec_cmd: + type: string + process_limit: + $ref: '#/definitions/models.CustomLimit' + project_name: + description: 'default project: "default"' + type: string + queue_name: + description: set to DEFAULT_QUEUE in config + type: string + requeue_error_values: + type: string + rerunable: + type: boolean + resreq: + description: 'default: "type == local"' + type: string + rsv_id: + type: string + run_limit: + allOf: + - $ref: '#/definitions/models.CustomInt' + description: k change type + run_time_estimation: + type: integer + shell_limit: + $ref: '#/definitions/models.ShellLimit' + sla: + type: string + stack_limit: + $ref: '#/definitions/models.CustomLimit' + swap_limit: + $ref: '#/definitions/models.CustomLimit' + tags: + items: + type: string + type: array + term_time: + allOf: + - $ref: '#/definitions/models.CustomTime' + description: k change type + thread_limit: + $ref: '#/definitions/models.CustomLimit' + transfer_files: + items: + type: string + type: array + user_group: + type: string + warning_action: + type: string + warning_time_period: + type: integer + required: + - command + type: object + models.ShellLimit: + properties: + core_limit: + $ref: '#/definitions/models.CustomLimit' + cpu_limit: + description: in milliseconds + type: integer + data_limit: + $ref: '#/definitions/models.CustomLimit' + file_limit: + $ref: '#/definitions/models.CustomLimit' + mem_limit: + $ref: '#/definitions/models.CustomLimit' + num_files: + type: integer + process_limit: + $ref: '#/definitions/models.CustomLimit' + stack_limit: + $ref: '#/definitions/models.CustomLimit' + swap_limit: + $ref: '#/definitions/models.CustomLimit' + thread_limit: + $ref: '#/definitions/models.CustomLimit' type: object host: localhost:8443 info: contact: {} - description: API Server - title: Host Management API + description: XCE cluster management REST API. + title: XCE API Server version: "1.0" paths: - /api/v1/hosts: + /hosts: get: consumes: - application/json - description: Get basic or full host information + description: Get host information + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.JsonStruct' + "400": + description: Bad Request + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + summary: Get host information + tags: + - hosts + /jobs: + get: + consumes: + - application/json + description: Get jobs information parameters: - - description: Host type (basic or full) + - description: filter, e.g. user:eq:user1,queue:eq:queue1 in: query - name: type - required: true + name: filter + type: string + - description: fields, e.g. jobid,status,queue + in: query + name: fields type: string produces: - application/json responses: "200": - description: Response for full host type + description: Response for jobs info schema: - items: - $ref: '#/definitions/models.FullHost' - type: array + $ref: '#/definitions/controllers.JsonStruct' "400": description: Bad Request schema: - $ref: '#/definitions/models.Error' - summary: Get host information + $ref: '#/definitions/controllers.JsonErrStruct' + summary: Get jobs information tags: - - hosts + - jobs + /jobs/{jobids}: + get: + consumes: + - application/json + description: Retrieve information for multiple jobs in a single request with + optional field selection + parameters: + - description: Comma-separated list of job IDs (e.g., 1001,1002,1003) + in: path + name: jobids + required: true + type: string + - description: Comma-separated list of fields to return (e.g., jobid,status,queue) + in: query + name: fields + type: string + produces: + - application/json + responses: + "200": + description: Successfully retrieved jobs + schema: + $ref: '#/definitions/controllers.JsonStruct' + "400": + description: invalid parameters + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "500": + description: Internal server error + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + summary: Get multiple jobs by their IDs + tags: + - jobs + /jobs/{jobids}/control: + post: + consumes: + - application/json + description: |- + Submit an action to be performed on a specific job identified by jobids. + kill without a signal: SIGINT, SIGTERM, then SIGKILL, job.terminate_interval + seconds apart while the job runs; with a signal: that signal once; with + force_kill: SIGKILL at once. stop: SIGTSTP, then SIGSTOP after the interval + if the job has not stopped. resume: SIGCONT (cancels a stop in progress). + parameters: + - description: Job ID + in: path + name: jobids + required: true + type: string + - description: Action to be performed + in: body + name: action + required: true + schema: + $ref: '#/definitions/models.JobControlJobIDRequest' + produces: + - application/json + responses: + "202": + description: Action successfully accepted + schema: + $ref: '#/definitions/controllers.JsonStruct' + "400": + description: Invalid request body or parameters + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "404": + description: Job not found + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "500": + description: Internal server error + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + summary: Perform an action on a job + tags: + - jobs + /jobs/control: + post: + consumes: + - application/json + description: |- + Submit an action to be performed on specific jobs identified by filter in request body. + kill/stop/resume signals as for POST /jobs/{jobids}/control. + parameters: + - description: Action to be performed + in: body + name: action + required: true + schema: + $ref: '#/definitions/models.JobControlRequest' + produces: + - application/json + responses: + "202": + description: Action successfully accepted + schema: + $ref: '#/definitions/controllers.JsonStruct' + "400": + description: Invalid request body or parameters + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "404": + description: Job not found + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "500": + description: Internal server error + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + summary: Perform an action on filtered out jobs + tags: + - jobs + /jobs/create: + post: + consumes: + - application/json + description: Submit a new job with specified requirements and parameters + parameters: + - description: Job submission parameters + in: body + name: job + required: true + schema: + $ref: '#/definitions/models.JobSubmitRequest' + produces: + - application/json + responses: + "201": + description: Job successfully created + schema: + $ref: '#/definitions/controllers.JsonStruct' + "400": + description: Invalid request body or parameters + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "401": + description: Unauthorized - Invalid or missing credentials + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "403": + description: Forbidden - User lacks permission + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + "500": + description: Internal server error + schema: + $ref: '#/definitions/controllers.JsonErrStruct' + summary: Create a new job + tags: + - jobs swagger: "2.0"