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) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-10 10:29:02 +00:00
parent 1ff068f6e7
commit f7fef278f6
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
6 changed files with 1700 additions and 684 deletions

View file

@ -64,6 +64,14 @@ func composeVersionDisplay(gitCommit, dirtyFingerprint string) string {
return strings.TrimSuffix(gitCommit, "-dirty") + "-" + dirtyFingerprint 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() { func main() {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
gin.DefaultWriter = io.Discard gin.DefaultWriter = io.Discard

View file

@ -26,9 +26,9 @@ type HostHandler struct {
// @Tags hosts // @Tags hosts
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Success 200 // @Success 200 {object} JsonStruct
// @Failure 400 {object} models.Error // @Failure 400 {object} JsonErrStruct
// @Router /api/v1/hosts [get] // @Router /hosts [get]
func (h *HostHandler) GetHostsInfo(ctx *gin.Context) { func (h *HostHandler) GetHostsInfo(ctx *gin.Context) {
logger.Debug("Enter GetAllHosts() function from ", zap.String("client_ip", ctx.ClientIP())) logger.Debug("Enter GetAllHosts() function from ", zap.String("client_ip", ctx.ClientIP()))

View file

@ -49,11 +49,11 @@ type JobController struct{}
// @Tags jobs // @Tags jobs
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param filter query string "filter=[{user:eq:user1},{queue:eq:queue1}]" // @Param filter query string false "filter, e.g. user:eq:user1,queue:eq:queue1"
// @Param fields query string "fields=jobid,status,queue" // @Param fields query string false "fields, e.g. jobid,status,queue"
// @Success 200 {array} models.JobInfo "Response for jobs info" // @Success 200 {object} JsonStruct "Response for jobs info"
// @Failure 400 {object} models.Error // @Failure 400 {object} JsonErrStruct
// @Router /api/v1/jobs [get] // @Router /jobs [get]
func (j JobController) GetJobsInfo(ctx *gin.Context) { func (j JobController) GetJobsInfo(ctx *gin.Context) {
logger.Debug("Enter GetJobsInfo() function from ", zap.String("client_ip", ctx.ClientIP())) logger.Debug("Enter GetJobsInfo() function from ", zap.String("client_ip", ctx.ClientIP()))
var fields []string var fields []string
@ -545,11 +545,9 @@ func returnSelectionError(ctx *gin.Context, raw string, err error) {
// @Produce json // @Produce json
// @Param jobids path string true "Comma-separated list of job IDs (e.g., 1001,1002,1003)" // @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)" // @Param fields query string false "Comma-separated list of fields to return (e.g., jobid,status,queue)"
// @Success 200 {object} JobsResponse "Successfully retrieved jobs" // @Success 200 {object} JsonStruct "Successfully retrieved jobs"
// @Failure 400 INVALID_PARAMS_CODE "invalid parameters" // @Failure 400 {object} JsonErrStruct "invalid parameters"
// @Failure 500 ERROR_MSG // @Failure 500 {object} JsonErrStruct "Internal server error"
// @Example {curl} Example Usage:
// @Example curl -X GET "http://localhost:8080/jobs/1001,1002,1003?fields=jobid,status,queue"
// @Router /jobs/{jobids} [get] // @Router /jobs/{jobids} [get]
func (j JobController) GetJobByIDs(ctx *gin.Context) { func (j JobController) GetJobByIDs(ctx *gin.Context) {
logger.Debug("Enter GetJobByIDs() function from ", zap.String("client_ip", ctx.ClientIP())) 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 // @Tags jobs
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param job body JobSubmitRequest true "Job submission parameters" // @Param job body models.JobSubmitRequest true "Job submission parameters"
// @Success 201 {object} JobSubmitResponse "Job successfully created" // @Success 201 {object} JsonStruct "Job successfully created"
// @Failure 400 {object} ErrorResponse "Invalid request body or parameters" // @Failure 400 {object} JsonErrStruct "Invalid request body or parameters"
// @Failure 401 {object} ErrorResponse "Unauthorized - Invalid or missing credentials" // @Failure 401 {object} JsonErrStruct "Unauthorized - Invalid or missing credentials"
// @Failure 403 {object} ErrorResponse "Forbidden - User lacks permission" // @Failure 403 {object} JsonErrStruct "Forbidden - User lacks permission"
// @Failure 500 {object} ErrorResponse "Internal server error" // @Failure 500 {object} JsonErrStruct "Internal server error"
// @Router /jobs [post] // @Router /jobs/create [post]
func (j JobController) CreateJob(ctx *gin.Context) { func (j JobController) CreateJob(ctx *gin.Context) {
logger.Debug("Enter CreateJob() function from ", zap.String("client_ip", ctx.ClientIP())) logger.Debug("Enter CreateJob() function from ", zap.String("client_ip", ctx.ClientIP()))
@ -2046,11 +2044,11 @@ func postJobRequest(job *models.JobInfo) error {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param jobids path string true "Job ID" // @Param jobids path string true "Job ID"
// @Param action body JobSignalRequest true "Action to be performed" // @Param action body models.JobControlJobIDRequest true "Action to be performed"
// @Success 202 {object} JobControlResponseSummary "Action successfully accepted" // @Success 202 {object} JsonStruct "Action successfully accepted"
// @Failure 400 {object} ErrorResponse "Invalid request body or parameters" // @Failure 400 {object} JsonErrStruct "Invalid request body or parameters"
// @Failure 404 {object} ErrorResponse "Job not found" // @Failure 404 {object} JsonErrStruct "Job not found"
// @Failure 500 {object} ErrorResponse "Internal server error" // @Failure 500 {object} JsonErrStruct "Internal server error"
// @Router /jobs/{jobids}/control [post] // @Router /jobs/{jobids}/control [post]
func (j JobController) ControlJobByID(ctx *gin.Context) { func (j JobController) ControlJobByID(ctx *gin.Context) {
logger.Debug("Enter ControlJobByID() function from ", zap.String("client_ip", ctx.ClientIP())) 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 // @Tags jobs
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param jobids path string true "Job ID" // @Param action body models.JobControlRequest true "Action to be performed"
// @Param action body JobControlRequest true "Action to be performed" // @Success 202 {object} JsonStruct "Action successfully accepted"
// @Success 202 {object} JobControlResponseSummary "Action successfully accepted" // @Failure 400 {object} JsonErrStruct "Invalid request body or parameters"
// @Failure 400 {object} ErrorResponse "Invalid request body or parameters" // @Failure 404 {object} JsonErrStruct "Job not found"
// @Failure 404 {object} ErrorResponse "Job not found" // @Failure 500 {object} JsonErrStruct "Internal server error"
// @Failure 500 {object} ErrorResponse "Internal server error"
// @Router /jobs/control [post] // @Router /jobs/control [post]
func (j JobController) ControlJob(ctx *gin.Context) { func (j JobController) ControlJob(ctx *gin.Context) {
logger.Debug("Enter ControlJob() function from ", zap.String("client_ip", ctx.ClientIP())) logger.Debug("Enter ControlJob() function from ", zap.String("client_ip", ctx.ClientIP()))

View file

@ -15,9 +15,9 @@ const docTemplate = `{
"host": "{{.Host}}", "host": "{{.Host}}",
"basePath": "{{.BasePath}}", "basePath": "{{.BasePath}}",
"paths": { "paths": {
"/api/v1/hosts": { "/hosts": {
"get": { "get": {
"description": "Get basic or full host information", "description": "Get host information",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -28,29 +28,279 @@ const docTemplate = `{
"hosts" "hosts"
], ],
"summary": "Get host information", "summary": "Get host information",
"parameters": [
{
"type": "string",
"description": "Host type (basic or full)",
"name": "type",
"in": "query",
"required": true
}
],
"responses": { "responses": {
"200": { "200": {
"description": "Response for full host type", "description": "OK",
"schema": { "schema": {
"type": "array", "$ref": "#/definitions/controllers.JsonStruct"
"items": {
"$ref": "#/definitions/models.FullHost"
}
} }
}, },
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "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": { "definitions": {
"models.BasicHost": { "controllers.JsonErrStruct": {
"type": "object", "type": "object",
"properties": { "properties": {
"DResources": { "code": {
"type": "array",
"items": {
"type": "string"
}
},
"busyThreshold": {
"type": "array",
"items": {
"type": "number"
}
},
"cores": {
"type": "integer" "type": "integer"
}, },
"cores_per_proc": { "details": {},
"msg": {}
}
},
"controllers.JsonStruct": {
"type": "object",
"properties": {
"code": {
"type": "integer" "type": "integer"
}, },
"cpuFactor": { "data": {},
"type": "number" "msg": {}
}, }
"hostAddr": { },
"type": "string" "models.CustomInt": {
}, "type": "object",
"hostModel": { "properties": {
"type": "string" "seconds": {
},
"hostName": {
"type": "string"
},
"hostType": {
"type": "string"
},
"isServer": {
"type": "boolean"
},
"maxCpus": {
"type": "integer" "type": "integer"
}, }
"maxMem": { }
"type": "integer" },
}, "models.CustomLimit": {
"maxSwap": { "type": "object",
"type": "integer" "properties": {
}, "mb": {
"maxTmp": { "type": "number",
"type": "integer" "format": "float64"
}, }
"nDRes": { }
"type": "integer" },
}, "models.CustomTime": {
"nDisks": { "type": "object",
"type": "integer" "properties": {
}, "time": {
"nRes": {
"type": "integer"
},
"numIndx": {
"type": "integer"
},
"pprocs": {
"type": "integer"
},
"resources": {
"type": "array",
"items": {
"type": "string"
}
},
"threads_per_core": {
"type": "integer"
},
"windows": {
"type": "string" "type": "string"
} }
} }
}, },
"models.Error": { "models.JobControlFilter": {
"type": "object", "type": "object",
"properties": { "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": "string"
} }
} }
}, },
"models.FullHost": { "models.JobControlJobIDRequest": {
"type": "object", "type": "object",
"required": [
"action"
],
"properties": { "properties": {
"DResources": { "action": {
"type": "array",
"items": {
"type": "string"
}
},
"affinityCpulist": {
"type": "string" "type": "string"
}, },
"attr": { "comment": {
"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": {
"type": "string" "type": "string"
}, },
"hostAddr": { "force_kill": {
"type": "string" "description": "kill: SIGKILL at once (no graceful sequence)",
},
"hostModel": {
"type": "string"
},
"hostName": {
"type": "string"
},
"hostType": {
"type": "string"
},
"idleTime": {
"type": "integer"
},
"isServer": {
"type": "boolean" "type": "boolean"
}, },
"load": { "method": {
"type": "array", "description": "\"criu\" as default",
"items": { "type": "string"
"type": "number"
}
}, },
"loadSched": { "signal": {
"type": "array", "description": "kill only: send this signal once instead of the graceful sequence",
"items": { "type": "string"
"type": "number" }
} }
},
"models.JobControlRequest": {
"type": "object",
"required": [
"action",
"filter"
],
"properties": {
"action": {
"type": "string"
}, },
"loadStop": { "comment": {
"type": "array", "type": "string"
"items": {
"type": "number"
}
}, },
"maxAvail": { "filter": {
"type": "number" "$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" "type": "integer"
}, },
"maxExitRate": { "command": {
"type": "integer" "type": "string"
}, },
"maxJobs": { "core_limit": {
"type": "integer" "$ref": "#/definitions/models.CustomLimit"
}, },
"maxMem": { "cpu_limit": {
"type": "integer" "description": "k change type",
"allOf": [
{
"$ref": "#/definitions/models.CustomInt"
}
]
}, },
"maxSwap": { "cwd": {
"type": "integer" "type": "string"
}, },
"maxTmp": { "data_limit": {
"type": "integer" "$ref": "#/definitions/models.CustomLimit"
}, },
"mig": { "data_spec_file": {
"type": "integer" "type": "string"
}, },
"nDRes": { "depend_cond": {
"type": "integer" "type": "string"
}, },
"nDisks": { "env": {
"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": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
} }
}, },
"threads_per_core": { "err_file": {
"type": "integer"
},
"userJobLimit": {
"type": "integer"
},
"windows": {
"type": "string" "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 // SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{ var SwaggerInfo = &swag.Spec{
Version: "1.0", Version: "1.0",
Host: "localhost:8443", Host: "localhost:8443",
BasePath: "/", BasePath: "/xce/v1",
Schemes: []string{}, Schemes: []string{},
Title: "Host Management API", Title: "XCE API Server",
Description: "API Server", Description: "XCE cluster management REST API.",
InfoInstanceName: "swagger", InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate, SwaggerTemplate: docTemplate,
LeftDelim: "{{", LeftDelim: "{{",
RightDelim: "}}", RightDelim: "}}",
} }
func init() { func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
} }

View file

@ -1,17 +1,17 @@
{ {
"swagger": "2.0", "swagger": "2.0",
"info": { "info": {
"description": "API Server", "description": "XCE cluster management REST API.",
"title": "Host Management API", "title": "XCE API Server",
"contact": {}, "contact": {},
"version": "1.0" "version": "1.0"
}, },
"host": "localhost:8443", "host": "localhost:8443",
"basePath": "/", "basePath": "/xce/v1",
"paths": { "paths": {
"/api/v1/hosts": { "/hosts": {
"get": { "get": {
"description": "Get basic or full host information", "description": "Get host information",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -22,29 +22,279 @@
"hosts" "hosts"
], ],
"summary": "Get host information", "summary": "Get host information",
"parameters": [
{
"type": "string",
"description": "Host type (basic or full)",
"name": "type",
"in": "query",
"required": true
}
],
"responses": { "responses": {
"200": { "200": {
"description": "Response for full host type", "description": "OK",
"schema": { "schema": {
"type": "array", "$ref": "#/definitions/controllers.JsonStruct"
"items": {
"$ref": "#/definitions/models.FullHost"
}
} }
}, },
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "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": { "definitions": {
"models.BasicHost": { "controllers.JsonErrStruct": {
"type": "object", "type": "object",
"properties": { "properties": {
"DResources": { "code": {
"type": "array",
"items": {
"type": "string"
}
},
"busyThreshold": {
"type": "array",
"items": {
"type": "number"
}
},
"cores": {
"type": "integer" "type": "integer"
}, },
"cores_per_proc": { "details": {},
"msg": {}
}
},
"controllers.JsonStruct": {
"type": "object",
"properties": {
"code": {
"type": "integer" "type": "integer"
}, },
"cpuFactor": { "data": {},
"type": "number" "msg": {}
}, }
"hostAddr": { },
"type": "string" "models.CustomInt": {
}, "type": "object",
"hostModel": { "properties": {
"type": "string" "seconds": {
},
"hostName": {
"type": "string"
},
"hostType": {
"type": "string"
},
"isServer": {
"type": "boolean"
},
"maxCpus": {
"type": "integer" "type": "integer"
}, }
"maxMem": { }
"type": "integer" },
}, "models.CustomLimit": {
"maxSwap": { "type": "object",
"type": "integer" "properties": {
}, "mb": {
"maxTmp": { "type": "number",
"type": "integer" "format": "float64"
}, }
"nDRes": { }
"type": "integer" },
}, "models.CustomTime": {
"nDisks": { "type": "object",
"type": "integer" "properties": {
}, "time": {
"nRes": {
"type": "integer"
},
"numIndx": {
"type": "integer"
},
"pprocs": {
"type": "integer"
},
"resources": {
"type": "array",
"items": {
"type": "string"
}
},
"threads_per_core": {
"type": "integer"
},
"windows": {
"type": "string" "type": "string"
} }
} }
}, },
"models.Error": { "models.JobControlFilter": {
"type": "object", "type": "object",
"properties": { "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": "string"
} }
} }
}, },
"models.FullHost": { "models.JobControlJobIDRequest": {
"type": "object", "type": "object",
"required": [
"action"
],
"properties": { "properties": {
"DResources": { "action": {
"type": "array",
"items": {
"type": "string"
}
},
"affinityCpulist": {
"type": "string" "type": "string"
}, },
"attr": { "comment": {
"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": {
"type": "string" "type": "string"
}, },
"hostAddr": { "force_kill": {
"type": "string" "description": "kill: SIGKILL at once (no graceful sequence)",
},
"hostModel": {
"type": "string"
},
"hostName": {
"type": "string"
},
"hostType": {
"type": "string"
},
"idleTime": {
"type": "integer"
},
"isServer": {
"type": "boolean" "type": "boolean"
}, },
"load": { "method": {
"type": "array", "description": "\"criu\" as default",
"items": { "type": "string"
"type": "number"
}
}, },
"loadSched": { "signal": {
"type": "array", "description": "kill only: send this signal once instead of the graceful sequence",
"items": { "type": "string"
"type": "number" }
} }
},
"models.JobControlRequest": {
"type": "object",
"required": [
"action",
"filter"
],
"properties": {
"action": {
"type": "string"
}, },
"loadStop": { "comment": {
"type": "array", "type": "string"
"items": {
"type": "number"
}
}, },
"maxAvail": { "filter": {
"type": "number" "$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" "type": "integer"
}, },
"maxExitRate": { "command": {
"type": "integer" "type": "string"
}, },
"maxJobs": { "core_limit": {
"type": "integer" "$ref": "#/definitions/models.CustomLimit"
}, },
"maxMem": { "cpu_limit": {
"type": "integer" "description": "k change type",
"allOf": [
{
"$ref": "#/definitions/models.CustomInt"
}
]
}, },
"maxSwap": { "cwd": {
"type": "integer" "type": "string"
}, },
"maxTmp": { "data_limit": {
"type": "integer" "$ref": "#/definitions/models.CustomLimit"
}, },
"mig": { "data_spec_file": {
"type": "integer" "type": "string"
}, },
"nDRes": { "depend_cond": {
"type": "integer" "type": "string"
}, },
"nDisks": { "env": {
"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": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
} }
}, },
"threads_per_core": { "err_file": {
"type": "integer"
},
"userJobLimit": {
"type": "integer"
},
"windows": {
"type": "string" "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"
} }
} }
} }

View file

@ -1,212 +1,467 @@
basePath: / basePath: /xce/v1
definitions: definitions:
models.BasicHost: controllers.JsonErrStruct:
properties: properties:
DResources: code:
items:
type: string
type: array
busyThreshold:
items:
type: number
type: array
cores:
type: integer type: integer
cores_per_proc: details: {}
msg: {}
type: object
controllers.JsonStruct:
properties:
code:
type: integer type: integer
cpuFactor: data: {}
msg: {}
type: object
models.CustomInt:
properties:
seconds:
type: integer
type: object
models.CustomLimit:
properties:
mb:
format: float64
type: number type: number
hostAddr: type: object
type: string models.CustomTime:
hostModel: properties:
type: string time:
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: string type: string
type: object type: object
models.Error: models.JobControlFilter:
properties: 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: string
type: object type: object
models.FullHost: models.JobControlJobIDRequest:
properties: properties:
DResources: action:
items:
type: string
type: array
affinityCpulist:
type: string type: string
attr: comment:
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:
type: string type: string
hostAddr: force_kill:
type: string description: 'kill: SIGKILL at once (no graceful sequence)'
hostModel:
type: string
hostName:
type: string
hostType:
type: string
idleTime:
type: integer
isServer:
type: boolean type: boolean
load: method:
items: description: '"criu" as default'
type: number type: string
type: array signal:
loadSched: description: 'kill only: send this signal once instead of the graceful sequence'
items: type: string
type: number required:
type: array - action
loadStop: type: object
items: models.JobControlRequest:
type: number properties:
type: array action:
maxAvail: type: string
type: number comment:
maxCpus: 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 type: integer
maxExitRate: command:
type: integer type: string
maxJobs: core_limit:
type: integer $ref: '#/definitions/models.CustomLimit'
maxMem: cpu_limit:
type: integer allOf:
maxSwap: - $ref: '#/definitions/models.CustomInt'
type: integer description: k change type
maxTmp: cwd:
type: integer type: string
mig: data_limit:
type: integer $ref: '#/definitions/models.CustomLimit'
nDRes: data_spec_file:
type: integer type: string
nDisks: depend_cond:
type: integer type: string
nIdx: env:
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:
items: items:
type: string type: string
type: array type: array
threads_per_core: err_file:
type: integer
userJobLimit:
type: integer
windows:
type: string 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 type: object
host: localhost:8443 host: localhost:8443
info: info:
contact: {} contact: {}
description: API Server description: XCE cluster management REST API.
title: Host Management API title: XCE API Server
version: "1.0" version: "1.0"
paths: paths:
/api/v1/hosts: /hosts:
get: get:
consumes: consumes:
- application/json - 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: parameters:
- description: Host type (basic or full) - description: filter, e.g. user:eq:user1,queue:eq:queue1
in: query in: query
name: type name: filter
required: true type: string
- description: fields, e.g. jobid,status,queue
in: query
name: fields
type: string type: string
produces: produces:
- application/json - application/json
responses: responses:
"200": "200":
description: Response for full host type description: Response for jobs info
schema: schema:
items: $ref: '#/definitions/controllers.JsonStruct'
$ref: '#/definitions/models.FullHost'
type: array
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/models.Error' $ref: '#/definitions/controllers.JsonErrStruct'
summary: Get host information summary: Get jobs information
tags: 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" swagger: "2.0"