Merge Go frontend (apiserver + xcecli) into meson build
This commit is contained in:
parent
43dc12a36c
commit
c9dc8c4676
205 changed files with 30793 additions and 328 deletions
22
src/frontend/xcecli/models/cluster.go
Executable file
22
src/frontend/xcecli/models/cluster.go
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
package models
|
||||
|
||||
type ClusterResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []ClusterInfo `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ClusterInfo struct {
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
ControllerName string `json:"controller_name"`
|
||||
Resources interface{} `json:"resources"`
|
||||
HostTypes interface{} `json:"host_types"`
|
||||
HostModels interface{} `json:"host_models"`
|
||||
ClusterAdmins interface{} `json:"cluster_admins"`
|
||||
}
|
||||
39
src/frontend/xcecli/models/exitstatus_test.go
Normal file
39
src/frontend/xcecli/models/exitstatus_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQueryJob_ExitStatus_Null(t *testing.T) {
|
||||
raw := `{"Fields":["job_id","exit_status"],"Data":{"job_id":1001,"exit_status":null}}`
|
||||
var j QueryJob
|
||||
if err := json.Unmarshal([]byte(raw), &j); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if j.ExitStatus != "" {
|
||||
t.Errorf("ExitStatus: got %q want empty", j.ExitStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryJob_ExitStatus_String(t *testing.T) {
|
||||
raw := `{"Fields":["job_id","exit_status"],"Data":{"job_id":1001,"exit_status":"{\"signal\":9}"}}`
|
||||
var j QueryJob
|
||||
if err := json.Unmarshal([]byte(raw), &j); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if j.ExitStatus == "" {
|
||||
t.Error("ExitStatus should not be empty for non-null value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryJob_ExitStatus_Zero(t *testing.T) {
|
||||
raw := `{"Fields":["job_id","exit_status"],"Data":{"job_id":1001,"exit_status":0}}`
|
||||
var j QueryJob
|
||||
if err := json.Unmarshal([]byte(raw), &j); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if j.ExitStatus != "0" {
|
||||
t.Errorf("ExitStatus: got %q want 0", j.ExitStatus)
|
||||
}
|
||||
}
|
||||
154
src/frontend/xcecli/models/hosts.go
Executable file
154
src/frontend/xcecli/models/hosts.go
Executable file
|
|
@ -0,0 +1,154 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
HEADER_HOST_NAME = "HOST_NAME"
|
||||
HEADER_HOST_STATUS = "STATUS"
|
||||
HEADER_HOST_NCPUS = "NCPUS"
|
||||
HEADER_HOST_MAX_SLOT = "MAX_SLOT"
|
||||
HEADER_HOST_NUM_RUN = "NUM_RUN"
|
||||
HEADER_HOST_NUM_SSUSP = "NUM_SSUSP"
|
||||
HEADER_HOST_NUM_USUSP = "NUM_USUSP"
|
||||
HEADER_HOST_HOST_TYPE = "HOST_TYPE"
|
||||
// host details info
|
||||
HEADER_HOST_CPUF = "CPUF"
|
||||
HEADER_HOST_JLU = "JL/U"
|
||||
HEADER_HOST_MAX = "MAX"
|
||||
HEADER_HOST_NJOBS = "NJOBS"
|
||||
HEADER_HOST_RUN = "RUN"
|
||||
HEADER_HOST_SSUSP = "SSUSP"
|
||||
HEADER_HOST_USUSP = "USUSP"
|
||||
HEADER_HOST_RSV = "RSV"
|
||||
HEADER_HOST_DISPT_WINDOW = "DISPATCH_WINDOW"
|
||||
// second line
|
||||
HEADER_HOST_R15S = "r15s"
|
||||
HEADER_HOST_R1M = "r1m"
|
||||
HEADER_HOST_R15M = "r15m"
|
||||
HEADER_HOST_UT = "ut"
|
||||
HEADER_HOST_PG = "pg"
|
||||
HEADER_HOST_IO = "io"
|
||||
HEADER_HOST_LS = "ls"
|
||||
HEADER_HOST_IT = "it"
|
||||
HEADER_HOST_TMP = "tmp"
|
||||
HEADER_HOST_SWP = "swp"
|
||||
HEADER_HOST_MEM = "mem"
|
||||
HEADER_HOST_SLOTS = "slots"
|
||||
)
|
||||
|
||||
var HostDetailHeaders = []string{
|
||||
HEADER_HOST_STATUS,
|
||||
HEADER_HOST_CPUF,
|
||||
HEADER_HOST_JLU,
|
||||
HEADER_HOST_MAX,
|
||||
HEADER_HOST_NJOBS,
|
||||
HEADER_HOST_RUN,
|
||||
HEADER_HOST_SSUSP,
|
||||
HEADER_HOST_USUSP,
|
||||
HEADER_HOST_RSV,
|
||||
HEADER_HOST_DISPT_WINDOW,
|
||||
}
|
||||
|
||||
var HostLoadSchedulingHeaders = []string{
|
||||
HEADER_HOST_R15S,
|
||||
HEADER_HOST_R1M,
|
||||
HEADER_HOST_R15M,
|
||||
HEADER_HOST_UT,
|
||||
HEADER_HOST_PG,
|
||||
HEADER_HOST_IO,
|
||||
HEADER_HOST_LS,
|
||||
HEADER_HOST_IT,
|
||||
HEADER_HOST_TMP,
|
||||
HEADER_HOST_SWP,
|
||||
HEADER_HOST_MEM,
|
||||
HEADER_HOST_SLOTS,
|
||||
}
|
||||
|
||||
// HostsResponse define the data structure in the host query response
|
||||
type HostsResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []XCEHost `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Host define the data structure in the host info\
|
||||
type XCEHost struct {
|
||||
HostID int64 `json:"host_id"`
|
||||
HostName string `json:"host_name"`
|
||||
HostStatus string `json:"host_status"`
|
||||
HostModel string `json:"host_model"`
|
||||
HostType string `json:"host_type"`
|
||||
HostGroup string `json:"host_group"`
|
||||
Flag int `json:"flag"`
|
||||
CPUFactor float64 `json:"cpu_factor"`
|
||||
DisptWindow interface{} `json:"dispt_window"`
|
||||
NumCPUs int `json:"num_cpus"`
|
||||
MaxSlot int `json:"max_slot"`
|
||||
MaxMem int `json:"max_mem"`
|
||||
MaxSwap int `json:"max_swap"`
|
||||
MaxTmp int `json:"max_tmp"`
|
||||
NumDisk int `json:"num_disk"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumDone int `json:"num_done"`
|
||||
AffTopo interface{} `json:"aff_topo"` // JSONB
|
||||
GPUTopo interface{} `json:"gpu_topo"` // JSONB
|
||||
SchedStop SchedStop `json:"sched_stop"` // JSONB
|
||||
RealLoad interface{} `json:"real_load"`
|
||||
}
|
||||
|
||||
var HostFieldsMap = map[string]string{
|
||||
HEADER_HOST_NAME: "host_name",
|
||||
HEADER_HOST_STATUS: "host_status",
|
||||
HEADER_HOST_NCPUS: "num_cores",
|
||||
HEADER_HOST_MAX_SLOT: "max_slot",
|
||||
HEADER_HOST_NUM_RUN: "num_run",
|
||||
HEADER_HOST_NUM_SSUSP: "num_ssusp",
|
||||
HEADER_HOST_NUM_USUSP: "num_ususp",
|
||||
HEADER_HOST_HOST_TYPE: "host_type",
|
||||
}
|
||||
|
||||
type SchedStop struct {
|
||||
Ls string `json:"ls"` // Load Sharing
|
||||
Pg string `json:"pg"` // Page
|
||||
R1m string `json:"r1m"` // Recent 1 minute
|
||||
Tmp string `json:"tmp"` // Temporary
|
||||
}
|
||||
|
||||
type HostControlRequest struct {
|
||||
ControlAction string `json:"action" binding:"required"` //control action
|
||||
Hostname []string `json:"host_name" binding:"required"` //host name
|
||||
Comment string `json:"comment"` //comment
|
||||
}
|
||||
|
||||
type HostControlResponse struct {
|
||||
Data struct {
|
||||
Accepted []*HostControlResponseItem `json:"accepted"`
|
||||
Failed []*HostControlResponseItem `json:"failed"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type HostControlResponseItem struct {
|
||||
HostName string `json:"host_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (h *XCEHost) UnmarshalJSON(data []byte) error {
|
||||
type Alias XCEHost
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*h = XCEHost(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResReqSelectRequest struct {
|
||||
Select string `json:"select"`
|
||||
Order string `json:"order,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
PageToken string `json:"page_token,omitempty"`
|
||||
}
|
||||
517
src/frontend/xcecli/models/jobs.go
Executable file
517
src/frontend/xcecli/models/jobs.go
Executable file
|
|
@ -0,0 +1,517 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
HEADER_JOB_JOBID = "JOBID"
|
||||
HEADER_JOB_USER = "USER"
|
||||
HEADER_JOB_STATUS = "STATUS"
|
||||
HEADER_JOB_QUEUE = "QUEUE"
|
||||
HEADER_JOB_FROM_HOST = "FROM_HOST"
|
||||
HEADER_JOB_EXEC_HOST = "EXEC_HOST"
|
||||
HEADER_JOB_COMMAND = "COMMAND"
|
||||
HEADER_JOB_NAME = "JOB_NAME"
|
||||
HEADER_JOB_SUBMIT_TIME = "SUBMIT_TIME"
|
||||
HEADER_JOB_CORE_LIMIT = "CORE_LIMIT"
|
||||
HEADER_JOB_MEM_LIMIT = "MEM_LIMIT"
|
||||
HEADER_JOB_PID = "PID"
|
||||
HEADER_JOB_PGID = "PGID"
|
||||
HEADER_JOB_PROJECT_NAME = "PROJECT_NAME"
|
||||
HEADER_JOB_START_TIME = "START_TIME"
|
||||
HEADER_JOB_END_TIME = "END_TIME"
|
||||
HEADER_JOB_CWD = "CWD"
|
||||
HEADER_JOB_CPU_TIME = "CPU_TIME"
|
||||
HEADER_JOB_RESREQ = "RESREQ"
|
||||
//HEADER_JOB_PRIORITY = "PRIORITY"
|
||||
HEADER_JOB_DESCRIPTION = "DESCRIPTION"
|
||||
HEADER_JOB_LAUNCH_TYPE = "LAUNCH_TYPE"
|
||||
HEADER_JOB_NUM_PROCESSORS = "NUM_PROCESSORS"
|
||||
HEADER_JOB_INTERACTIVE_URL = "INTERACTIVE_URL"
|
||||
|
||||
// second line
|
||||
HEADER_JOB_R15S = "r15s"
|
||||
HEADER_JOB_R1M = "r1m"
|
||||
HEADER_JOB_R15M = "r15m"
|
||||
HEADER_JOB_UT = "ut"
|
||||
HEADER_JOB_PG = "pg"
|
||||
HEADER_JOB_IO = "io"
|
||||
HEADER_JOB_LS = "ls"
|
||||
HEADER_JOB_IT = "it"
|
||||
HEADER_JOB_TMP = "tmp"
|
||||
HEADER_JOB_SWP = "swp"
|
||||
HEADER_JOB_MEM = "mem"
|
||||
)
|
||||
|
||||
var JobLoadSchedulingHeaders = []string{
|
||||
HEADER_JOB_R15S,
|
||||
HEADER_JOB_R1M,
|
||||
HEADER_JOB_R15M,
|
||||
HEADER_JOB_UT,
|
||||
HEADER_JOB_PG,
|
||||
HEADER_JOB_IO,
|
||||
HEADER_JOB_LS,
|
||||
HEADER_JOB_IT,
|
||||
HEADER_JOB_TMP,
|
||||
HEADER_JOB_SWP,
|
||||
HEADER_JOB_MEM,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// for array jobs
|
||||
const (
|
||||
HEADER_ARRAY_JOB_JOBID = "JOBID"
|
||||
HEADER_ARRAY_JOB_SPEC = "ARRAY_SPEC"
|
||||
HEADER_ARRAY_JOB_OWNER = "OWNER"
|
||||
HEADER_ARRAY_JOB_NJOBS = "NJOBS"
|
||||
HEADER_ARRAY_JOB_PEND = "PEND"
|
||||
HEADER_ARRAY_JOB_DONE = "DONE"
|
||||
HEADER_ARRAY_JOB_RUN = "RUN"
|
||||
HEADER_ARRAY_JOB_EXIT = "EXIT"
|
||||
HEADER_ARRAY_JOB_SSUSP = "SSUSP"
|
||||
HEADER_ARRAY_JOB_USUSP = "USUSP"
|
||||
HEADER_ARRAY_JOB_PSUSP = "PSUSP"
|
||||
)
|
||||
|
||||
type JobsResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []QueryJob `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type Jsonb struct {
|
||||
String string
|
||||
}
|
||||
|
||||
// Job define the data structure in the job info
|
||||
type QueryJob struct {
|
||||
JobID int64 `json:"job_id"`
|
||||
SubmitUser string `json:"submit_user"`
|
||||
JobStatus string `json:"job_status"`
|
||||
JobName string `json:"job_name"`
|
||||
Command string `json:"command"`
|
||||
QueueName string `json:"queue_name"`
|
||||
ProjectName string `json:"project_name"`
|
||||
UserGroup string `json:"user_group"`
|
||||
LicenseProject string `json:"license_project"`
|
||||
JobDescription string `json:"job_description"`
|
||||
Resreq string `json:"resreq"`
|
||||
RsvID string `json:"rsv_id"`
|
||||
SLA string `json:"sla"`
|
||||
MaxNumProcessors int `json:"maxnum_processors"`
|
||||
NumProcessors int `json:"num_processors"`
|
||||
AskedHosts string `json:"asked_hosts"`
|
||||
BeginTime string `json:"begin_time"`
|
||||
TermTime string `json:"term_time"`
|
||||
UserPriority int `json:"user_priority"`
|
||||
DependCond string `json:"depend_cond"`
|
||||
SubmitTime string `json:"submit_time"`
|
||||
ReserveTime time.Time `json:"reserve_time"`
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
JobRusage string `json:"job_rusage"`
|
||||
MaxMem float64 `json:"max_mem"`
|
||||
AvgMem float64 `json:"avg_mem"`
|
||||
UsedMem float64 `json:"used_mem"`
|
||||
CPUTime int `json:"cpu_time"`
|
||||
PeakCPUTime int `json:"peak_cpu_time"`
|
||||
PeakCPUDuration float64 `json:"peak_cpu_duration"`
|
||||
MemEfficiency float64 `json:"mem_efficiency"` // Memory efficiency in percentage
|
||||
AvgCPUEfficiency float64 `json:"avg_cpu_efficiency"` // Average CPU efficiency in percentage
|
||||
PeakCPUEfficiency float64 `json:"peak_cpu_efficiency"` // Peak CPU efficiency in percentage
|
||||
NumHosts int `json:"num_hosts"` // Number of hosts used by the job
|
||||
FirstHost string `json:"first_host"` // First host where the job ran
|
||||
AllocSlot string `json:"alloc_slots"` // Allocated slot information
|
||||
Duration int `json:"duration"`
|
||||
Umask int `json:"umask"`
|
||||
CWD string `json:"cwd"`
|
||||
SubmitHomeDir string `json:"submit_home_dir"`
|
||||
FromHost string `json:"from_host"`
|
||||
ExecHosts ExecHosts `json:"exec_hosts"`
|
||||
ExitStatus ExitStatusValue `json:"exit_status"`
|
||||
ExecUID int `json:"exec_uid"`
|
||||
ExecHome string `json:"exec_home"`
|
||||
ExecCWD string `json:"exec_cwd"`
|
||||
ExecUserName string `json:"exec_user_name"`
|
||||
JobPriority int `json:"job_priority"`
|
||||
ChargedSAAP string `json:"charged_SAAP"`
|
||||
PreExecCmd string `json:"pre_exec_cmd"`
|
||||
PostExecCmd string `json:"post_exec_cmd"`
|
||||
RequeueErrorValues string `json:"requeue_error_values"`
|
||||
NumLicenses int `json:"num_licenses"`
|
||||
LicenseNames []string `json:"license_names"`
|
||||
RunTime int `json:"run_time"`
|
||||
RunTimeEstimation int `json:"run_time_estimation"`
|
||||
MigThreshold int `json:"mig_threshold"`
|
||||
ChkpntPeriod int `json:"chkpnt_period"`
|
||||
ChkpntDir string `json:"chkpnt_dir"`
|
||||
OutDir string `json:"out_dir"`
|
||||
InFile string `json:"in_file"`
|
||||
OutFile string `json:"out_file"`
|
||||
ErrFile string `json:"err_file"`
|
||||
MailUser string `json:"mail_user"`
|
||||
LoginShell string `json:"login_shell"`
|
||||
ExceptList string `json:"except_list"`
|
||||
RLimits64 []int64 `json:"rlimits64"`
|
||||
SubEnvVars string `json:"sub_env_vars"`
|
||||
PendingReason string `json:"pending_reason"`
|
||||
JobStartInfo string `json:"job_start_info"`
|
||||
JobExecInfo string `json:"job_exec_info"`
|
||||
CoreLimit float64 `json:"core_limit"`
|
||||
CPULimit string `json:"cpu_limit"`
|
||||
RunLimit string `json:"run_limit"`
|
||||
FileLimit float64 `json:"file_limit"`
|
||||
DataLimit float64 `json:"data_limit"`
|
||||
StackLimit float64 `json:"stack_limit"`
|
||||
MemLimit float64 `json:"mem_limit"`
|
||||
SwapLimit float64 `json:"swap_limit"`
|
||||
ProcessLimit float64 `json:"process_limit"`
|
||||
ThreadLimit float64 `json:"thread_limit"`
|
||||
NumFiles int `json:"num_files"`
|
||||
ArrayIdx int `json:"array_idx"` //
|
||||
UniqueJobID int64 `json:"unique_job_id"` //
|
||||
ExtendData JobExtendData `json:"extend_data"`
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
PGIDs IntSlice `json:"pgids"`
|
||||
PID IntSlice `json:"pids"`
|
||||
LaunchType string `json:"launch_type"`
|
||||
}
|
||||
|
||||
var JobFieldsMap = map[string]string{
|
||||
HEADER_JOB_JOBID: "job_id",
|
||||
HEADER_JOB_USER: "submit_user",
|
||||
HEADER_JOB_STATUS: "job_status",
|
||||
HEADER_JOB_QUEUE: "queue_name",
|
||||
HEADER_JOB_COMMAND: "command",
|
||||
HEADER_JOB_NAME: "job_name",
|
||||
HEADER_JOB_SUBMIT_TIME: "submit_time",
|
||||
HEADER_JOB_FROM_HOST: "from_host",
|
||||
HEADER_JOB_EXEC_HOST: "exec_hosts",
|
||||
HEADER_JOB_CORE_LIMIT: "core_limit",
|
||||
HEADER_JOB_MEM_LIMIT: "mem_limit",
|
||||
HEADER_JOB_PID: "pids",
|
||||
HEADER_JOB_PGID: "pgids",
|
||||
HEADER_JOB_PROJECT_NAME: "project_name",
|
||||
HEADER_JOB_START_TIME: "start_time",
|
||||
HEADER_JOB_END_TIME: "end_time",
|
||||
HEADER_JOB_CWD: "cwd",
|
||||
HEADER_JOB_CPU_TIME: "cpu_time",
|
||||
HEADER_JOB_RESREQ: "resreq",
|
||||
HEADER_JOB_DESCRIPTION: "job_description",
|
||||
HEADER_JOB_LAUNCH_TYPE: "launch_type",
|
||||
HEADER_JOB_NUM_PROCESSORS: "num_processors",
|
||||
HEADER_JOB_INTERACTIVE_URL: "interactive_job_url",
|
||||
//job_filter.HEADER_JOB_PRIORITY: "job_priority",
|
||||
}
|
||||
|
||||
type IntSlice []int
|
||||
|
||||
func (s *IntSlice) UnmarshalJSON(data []byte) error {
|
||||
// parse array
|
||||
var arr []int
|
||||
if err := json.Unmarshal(data, &arr); err == nil {
|
||||
*s = arr
|
||||
return nil
|
||||
}
|
||||
// parse {}
|
||||
var obj map[string]interface{}
|
||||
if err := json.Unmarshal(data, &obj); err == nil {
|
||||
|
||||
if len(obj) == 0 {
|
||||
*s = []int{}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("cannot unmarshal IntSlice %s", string(data))
|
||||
}
|
||||
|
||||
// ExecHosts represents the execution hosts for a job
|
||||
type ExecHosts struct {
|
||||
HostSlots []HostSlot `json:"host_slots"`
|
||||
NumHosts int `json:"num_hosts"`
|
||||
}
|
||||
|
||||
// String returns the string representation of ExecHosts
|
||||
// If HostSlots is empty, return "-"
|
||||
// Otherwise, return the JSON formatted string
|
||||
func (e ExecHosts) String() string {
|
||||
if len(e.HostSlots) == 0 {
|
||||
return "-"
|
||||
}
|
||||
|
||||
// Use json.Marshal to convert the structure to a JSON string
|
||||
data, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return "-"
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// UnmarshalJSON customizes the JSON parsing method, compatible with both string "-" and structure formats
|
||||
func (e *ExecHosts) UnmarshalJSON(data []byte) error {
|
||||
// First try to parse as a string
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
// If the string is "-", return an empty ExecHosts
|
||||
if str == "-" {
|
||||
e.HostSlots = []HostSlot{}
|
||||
e.NumHosts = 0
|
||||
return nil
|
||||
}
|
||||
// If it's any other string, treat it as a single hostname
|
||||
e.HostSlots = []HostSlot{{HostName: str, NumSlots: 1}}
|
||||
e.NumHosts = 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// If it's not a string, try to parse as an ExecHosts structure
|
||||
type Alias ExecHosts
|
||||
aux := &struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(e),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HostSlot represents a single host slot
|
||||
type HostSlot struct {
|
||||
HostName string `json:"host_name"`
|
||||
NumSlots int `json:"num_slots"`
|
||||
}
|
||||
|
||||
type JobSubmitRequest struct {
|
||||
JobName string `json:"job_name,omitempty" binding:"required"`
|
||||
Command string `json:"command" binding:"required"`
|
||||
QueueName string `json:"queue_name,omitempty"` // set to DEFAULT_QUEUE in config
|
||||
ProjectName string `json:"project_name,omitempty"` // default project: "default"
|
||||
UserGroup string `json:"user_group,omitempty"`
|
||||
LicenseProject string `json:"license_project,omitempty"`
|
||||
JobDescription string `json:"job_description,omitempty"`
|
||||
Resreq string `json:"resreq,omitempty"` // default: "type == local"
|
||||
Gpureq string `json:"gpureq,omitempty"`
|
||||
RsvID string `json:"rsv_id,omitempty"`
|
||||
SLA string `json:"sla,omitempty"`
|
||||
MaxNumProcessors int `json:"maxnum_processors,omitempty"`
|
||||
NumProcessors int `json:"num_processors,omitempty"`
|
||||
AskedHosts string `json:"asked_hosts,omitempty"`
|
||||
BeginTime string `json:"begin_time,omitempty"`
|
||||
TermTime string `json:"term_time,omitempty"`
|
||||
UserPriority int `json:"user_priority,omitempty"`
|
||||
DependCond string `json:"depend_cond,omitempty"`
|
||||
JobGroup string `json:"job_group,omitempty"`
|
||||
HostSpec string `json:"host_spec,omitempty"`
|
||||
Env []string `json:"env,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Exclusive bool `json:"exclusive,omitempty"`
|
||||
Rerunable bool `json:"rerunable,omitempty"`
|
||||
Interactive int `json:"interactive,omitempty"`
|
||||
WarningTimePeriod int `json:"warning_time_period,omitempty"`
|
||||
WarningAction string `json:"warning_action,omitempty"`
|
||||
DataSpecFile string `json:"data_spec_file,omitempty"`
|
||||
TransferFiles []string `json:"transfer_files,omitempty"`
|
||||
PreExecCmd string `json:"pre_exec_cmd,omitempty"`
|
||||
PostExecCmd string `json:"post_exec_cmd,omitempty"`
|
||||
RequeueErrorValues string `json:"requeue_error_values,omitempty"`
|
||||
RunTimeEstimation int `json:"run_time_estimation,omitempty"`
|
||||
MigThreshold int `json:"mig_threshold,omitempty"`
|
||||
ChkpntPeriod int `json:"chkpnt_period,omitempty"`
|
||||
ChkpntDir string `json:"chkpnt_dir,omitempty"`
|
||||
InitChkpntPeriod int `json:"init_chkpnt_period,omitempty"`
|
||||
CWD string `json:"cwd,omitempty"`
|
||||
OutDir string `json:"out_dir,omitempty"`
|
||||
InFile string `json:"in_file,omitempty"`
|
||||
OutFile string `json:"out_file,omitempty"`
|
||||
ErrFile string `json:"err_file,omitempty"`
|
||||
Mailuser string `json:"mailuser,omitempty"`
|
||||
LoginShell string `json:"login_shell,omitempty"`
|
||||
ExceptList string `json:"except_list,omitempty"`
|
||||
CPULimit string `json:"cpu_limit,omitempty"`
|
||||
RunLimit string `json:"run_limit,omitempty"`
|
||||
MemLimit string `json:"mem_limit,omitempty"`
|
||||
SwapLimit string `json:"swap_limit,omitempty"`
|
||||
CoreLimit string `json:"core_limit,omitempty"`
|
||||
DataLimit string `json:"data_limit,omitempty"`
|
||||
FileLimit string `json:"file_limit,omitempty"`
|
||||
ProcessLimit string `json:"process_limit,omitempty"`
|
||||
StackLimit string `json:"stack_limit,omitempty"`
|
||||
ThreadLimit string `json:"thread_limit,omitempty"`
|
||||
ShellLimit string `json:"shell_limit,omitempty"`
|
||||
NumFiles int `json:"num_files,omitempty"`
|
||||
LaunchType string `json:"launch_type,omitempty"`
|
||||
}
|
||||
|
||||
// JobSubmitData define the data structure in the job submit response
|
||||
type JobSubmitData struct {
|
||||
JobID int64 `json:"jobid"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// JobSubmitResponse define the data structure in the job submit response
|
||||
type JobSubmitResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data JobSubmitData `json:"data"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// UploadedFile represents the uploaded file
|
||||
type UploadedFile struct {
|
||||
DestFile string `json:"dest_file"` // dest file path
|
||||
SrcFile string `json:"src_file"` // src file name
|
||||
WebURL string `json:"web_url"` // file URL
|
||||
}
|
||||
|
||||
// UploadResponse represents the file upload response
|
||||
type UploadResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
UploadedFiles []UploadedFile `json:"accepted"`
|
||||
Failed []FailedFile `json:"failed"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FailedFile struct {
|
||||
FileName string `json:"file_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// ------- job control -------------
|
||||
type JobControlFilter struct {
|
||||
JobName string `json:"job_name,omitempty"`
|
||||
JobGroupName string `json:"job_group_name,omitempty"`
|
||||
QueueName string `json:"queue_name,omitempty"`
|
||||
Users string `json:"users,omitempty"`
|
||||
ExecHosts string `json:"exec_hosts,omitempty"`
|
||||
SLA string `json:"sla,omitempty"`
|
||||
AllJobs bool `json:"all_jobs,omitempty"`
|
||||
}
|
||||
|
||||
type JobControlRequest struct {
|
||||
Filter JobControlFilter `json:"filter" binding:"required"`
|
||||
Action string `json:"action" binding:"required"`
|
||||
Signal string `json:"signal,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
ForceKill bool `json:"force_kill,omitempty"`
|
||||
}
|
||||
|
||||
type JobControlJobIDRequest struct {
|
||||
Action string `json:"action" binding:"required"`
|
||||
Signal string `json:"signal,omitempty"` // signal value
|
||||
Comment string `json:"comment,omitempty"`
|
||||
ForceKill bool `json:"force_kill,omitempty"`
|
||||
}
|
||||
|
||||
type JobRestartRequest struct {
|
||||
AskedHosts string `json:"asked_hosts,omitempty"`
|
||||
}
|
||||
|
||||
type JobRestartResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data *JobRestartResponseData `json:"data"`
|
||||
}
|
||||
type JobRestartResponseData struct {
|
||||
JobID int64 `json:"jobid"`
|
||||
QueueName string `json:"queue_name"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type JobControlResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data *JobControlResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type JobControlResponseData struct {
|
||||
Accepted []*JobControlResponseItem `json:"accepted"`
|
||||
Failed []*JobControlResponseItem `json:"failed"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type JobControlResponseItem struct {
|
||||
JobID int64 `json:"job_id"`
|
||||
ArrayIdx int `json:"array_idx,omitempty"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// job array statistics data structure
|
||||
type ArrayJobStatsData struct {
|
||||
JobID int64 `json:"job_id"`
|
||||
ArrayJobName string `json:"array_job_name"`
|
||||
SubmitUser string `json:"submit_user"`
|
||||
NumJobs int `json:"num_jobs"`
|
||||
NumDone int `json:"num_done"`
|
||||
NumExit int `json:"num_exit"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumPend int `json:"num_pend"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumPSusp int `json:"num_psusp"`
|
||||
}
|
||||
|
||||
// job array statistics response data structure
|
||||
type ArrayJobsStatsResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []ArrayJobStatsData `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type JobExtendData struct {
|
||||
Interactive int `json:"interactive"`
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
Limits struct {
|
||||
CoreLimit int `json:"core_limit"`
|
||||
} `json:"limits"`
|
||||
}
|
||||
|
||||
func (j *QueryJob) UnmarshalJSON(data []byte) error {
|
||||
type Alias QueryJob
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*j = QueryJob(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExitStatusValue handles exit_status which can be null, a number, or a JSON string.
|
||||
type ExitStatusValue string
|
||||
|
||||
func (e *ExitStatusValue) UnmarshalJSON(data []byte) error {
|
||||
s := string(data)
|
||||
if s == "null" || s == "" {
|
||||
*e = ""
|
||||
return nil
|
||||
}
|
||||
// strip quotes if it's a JSON string
|
||||
if len(s) >= 2 && s[0] == '"' {
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
*e = ExitStatusValue(str)
|
||||
return nil
|
||||
}
|
||||
// bare number or other value — store as-is
|
||||
*e = ExitStatusValue(s)
|
||||
return nil
|
||||
}
|
||||
19
src/frontend/xcecli/models/logon.go
Executable file
19
src/frontend/xcecli/models/logon.go
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
// LogonData define the data structure in the logon response
|
||||
type LogonData struct {
|
||||
Token string `json:"token"`
|
||||
Path string `json:"path"`
|
||||
IP string `json:"ip"`
|
||||
CreateTime int64 `json:"createtime"`
|
||||
TokenExpireTime int64 `json:"token_expire_time"`
|
||||
}
|
||||
|
||||
// LogonResponse define the data structure in the logon response
|
||||
type LogonResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data LogonData `json:"data"`
|
||||
Count int `json:"count"`
|
||||
CACert string `json:"cacert"`
|
||||
}
|
||||
19
src/frontend/xcecli/models/ordermap.go
Normal file
19
src/frontend/xcecli/models/ordermap.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// UnmarshalOrderMapRow extracts the Data sub-object from the server's OrderMap wire format
|
||||
// {"Fields":[...],"Data":{...}} and unmarshals it into dst.
|
||||
// If the input is not an OrderMap wrapper, it falls back to direct unmarshal.
|
||||
func UnmarshalOrderMapRow(data []byte, dst interface{}) error {
|
||||
var wrapper struct {
|
||||
Data json.RawMessage `json:"Data"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &wrapper); err != nil {
|
||||
return err
|
||||
}
|
||||
if wrapper.Data == nil {
|
||||
return json.Unmarshal(data, dst)
|
||||
}
|
||||
return json.Unmarshal(wrapper.Data, dst)
|
||||
}
|
||||
13
src/frontend/xcecli/models/permissions.go
Normal file
13
src/frontend/xcecli/models/permissions.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package models
|
||||
|
||||
type PermsResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data []string `json:"data"`
|
||||
}
|
||||
|
||||
type Permission struct {
|
||||
PermissionName string `json:"permission_name" binding:"required"`
|
||||
DomainType string `json:"domain_type" binding:"required"`
|
||||
DomainValues []string `json:"domain_values"`
|
||||
}
|
||||
131
src/frontend/xcecli/models/queues.go
Executable file
131
src/frontend/xcecli/models/queues.go
Executable file
|
|
@ -0,0 +1,131 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
HEADER_QUEUE_NAME = "QUEUE_NAME"
|
||||
HEADER_QUEUE_PRIORITY = "PRIORITY"
|
||||
HEADER_QUEUE_STATUS = "QUEUE_STATUS"
|
||||
HEADER_QUEUE_NUM_PEND = "NUM_PEND"
|
||||
HEADER_QUEUE_NUM_RUN = "NUM_RUN"
|
||||
HEADER_QUEUE_NUM_SSUSP = "NUM_SSUSP"
|
||||
HEADER_QUEUE_NUM_USUSP = "NUM_USUSP"
|
||||
HEADER_QUEUE_NUM_DONE = "NUM_DONE"
|
||||
// queue detail info
|
||||
HEADER_QUEUE_PRIO = "PRIO"
|
||||
HEADER_QUEUE_NICE = "NICE"
|
||||
HEADER_QUEUE_MAX = "MAX"
|
||||
HEADER_QUEUE_JLU = "JL/U"
|
||||
HEADER_QUEUE_JLP = "JL/P"
|
||||
HEADER_QUEUE_JLH = "JL/H"
|
||||
HEADER_QUEUE_NJOBS = "NJOBS"
|
||||
HEADER_QUEUE_PEND = "PEND"
|
||||
HEADER_QUEUE_RUN = "RUN"
|
||||
HEADER_QUEUE_SSUSP = "SSUSP"
|
||||
HEADER_QUEUE_USUSP = "USUSP"
|
||||
HEADER_QUEUE_RSV = "RSV"
|
||||
// second line
|
||||
HEADER_QUEUE_R15S = "r15s"
|
||||
HEADER_QUEUE_R1M = "r1m"
|
||||
HEADER_QUEUE_R15M = "r15m"
|
||||
HEADER_QUEUE_UT = "ut"
|
||||
HEADER_QUEUE_PG = "pg"
|
||||
HEADER_QUEUE_IO = "io"
|
||||
HEADER_QUEUE_LS = "ls"
|
||||
HEADER_QUEUE_IT = "it"
|
||||
HEADER_QUEUE_TMP = "tmp"
|
||||
HEADER_QUEUE_SWP = "swp"
|
||||
HEADER_QUEUE_MEM = "mem"
|
||||
)
|
||||
|
||||
var QueueDetailHeaders = []string{
|
||||
HEADER_QUEUE_PRIO,
|
||||
HEADER_QUEUE_NICE,
|
||||
HEADER_QUEUE_STATUS,
|
||||
HEADER_QUEUE_MAX,
|
||||
HEADER_QUEUE_JLU,
|
||||
HEADER_QUEUE_JLP,
|
||||
HEADER_QUEUE_JLH,
|
||||
HEADER_QUEUE_NJOBS,
|
||||
HEADER_QUEUE_PEND,
|
||||
HEADER_QUEUE_RUN,
|
||||
HEADER_QUEUE_SSUSP,
|
||||
HEADER_QUEUE_USUSP,
|
||||
HEADER_QUEUE_RSV,
|
||||
}
|
||||
|
||||
var QueueLoadSchedulingHeaders = []string{
|
||||
HEADER_QUEUE_R15S,
|
||||
HEADER_QUEUE_R1M,
|
||||
HEADER_QUEUE_R15M,
|
||||
HEADER_QUEUE_UT,
|
||||
HEADER_QUEUE_PG,
|
||||
HEADER_QUEUE_IO,
|
||||
HEADER_QUEUE_LS,
|
||||
HEADER_QUEUE_IT,
|
||||
HEADER_QUEUE_TMP,
|
||||
HEADER_QUEUE_SWP,
|
||||
HEADER_QUEUE_MEM,
|
||||
}
|
||||
|
||||
// QueuesResponse define the data structure in the queue query response
|
||||
type QueuesResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []DBQueueInfo `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
var QueueFieldsMap = map[string]string{
|
||||
HEADER_QUEUE_NAME: "queue_name",
|
||||
HEADER_QUEUE_PRIORITY: "priority",
|
||||
HEADER_QUEUE_STATUS: "queue_status",
|
||||
HEADER_QUEUE_NUM_PEND: "num_pend",
|
||||
HEADER_QUEUE_NUM_RUN: "num_run",
|
||||
HEADER_QUEUE_NUM_SSUSP: "num_ssusp",
|
||||
HEADER_QUEUE_NUM_USUSP: "num_ususp",
|
||||
HEADER_QUEUE_NUM_DONE: "num_done",
|
||||
}
|
||||
|
||||
// DBQueueInfo define the data structure in the queue info
|
||||
type DBQueueInfo struct {
|
||||
QueueName string `json:"queue_name"`
|
||||
QueueStatus string `json:"queue_status"`
|
||||
Priority int `json:"priority"`
|
||||
Description string `json:"description"`
|
||||
NumPend int `json:"num_pend"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumDone int `json:"num_done"`
|
||||
FSInfo map[string]interface{} `json:"fs_info"` // JSONB
|
||||
}
|
||||
|
||||
type QueueControlRequest struct {
|
||||
ControlAction string `json:"action" binding:"required"` //control action
|
||||
QueueName []string `json:"queue_name" binding:"required"` //queue name
|
||||
Comment string `json:"comment"` //comment
|
||||
}
|
||||
|
||||
type QueueControlResponse struct {
|
||||
Data struct {
|
||||
Accepted []*QueueControlResponseItem `json:"accepted"`
|
||||
Failed []*QueueControlResponseItem `json:"failed"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type QueueControlResponseItem struct {
|
||||
QueueName string `json:"queue_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (q *DBQueueInfo) UnmarshalJSON(data []byte) error {
|
||||
type Alias DBQueueInfo
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*q = DBQueueInfo(a)
|
||||
return nil
|
||||
}
|
||||
202
src/frontend/xcecli/models/resource.go
Normal file
202
src/frontend/xcecli/models/resource.go
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
HEADER_RESOURCE_NAME = "RESOURCE"
|
||||
HEADER_RESOURCE_VALUE = "VALUE"
|
||||
HEADER_RESOURCE_LOC = "LOCATION"
|
||||
HEADER_RESOURCE_DEF_TYPE = "TYPE"
|
||||
HEADER_RESOURCE_DEF_ORDER = "ORDER"
|
||||
HEADER_RESOURCE_DEF_BUILTIN = "BUILTIN"
|
||||
HEADER_RESOURCE_DEF_CONSUMABLE = "CONSUMABLE"
|
||||
HEADER_RESOURCE_DEF_DYNAMIC = "DYNAMIC"
|
||||
HEADER_RESOURCE_DEF_RELEASE = "RELEASE"
|
||||
HEADER_RESOURCE_DEF_INTERVAL = "INTERVAL"
|
||||
HEADER_RESOURCE_DEF_DESC = "DESCRIPTION"
|
||||
)
|
||||
|
||||
var ResourceDistFieldsMap = map[string]string{
|
||||
HEADER_RESOURCE_NAME: "res_name",
|
||||
HEADER_RESOURCE_VALUE: "value",
|
||||
HEADER_RESOURCE_LOC: "location",
|
||||
}
|
||||
|
||||
// ResourceDefFieldsMap maps resource definition field headers to API field names
|
||||
var ResourceDefFieldsMap = map[string]string{
|
||||
HEADER_RESOURCE_NAME: "res_name",
|
||||
HEADER_RESOURCE_DEF_DESC: "description",
|
||||
HEADER_RESOURCE_DEF_TYPE: "type",
|
||||
HEADER_RESOURCE_DEF_ORDER: "order",
|
||||
HEADER_RESOURCE_DEF_BUILTIN: "builtin",
|
||||
HEADER_RESOURCE_DEF_CONSUMABLE: "consumable",
|
||||
HEADER_RESOURCE_DEF_DYNAMIC: "dynamic",
|
||||
HEADER_RESOURCE_DEF_RELEASE: "release",
|
||||
HEADER_RESOURCE_DEF_INTERVAL: "interval",
|
||||
}
|
||||
|
||||
var ResourceDefDetailHeaders = []string{
|
||||
HEADER_RESOURCE_DEF_TYPE,
|
||||
HEADER_RESOURCE_DEF_ORDER,
|
||||
HEADER_RESOURCE_DEF_INTERVAL,
|
||||
HEADER_RESOURCE_DEF_BUILTIN,
|
||||
HEADER_RESOURCE_DEF_DYNAMIC,
|
||||
HEADER_RESOURCE_DEF_RELEASE,
|
||||
HEADER_RESOURCE_DEF_CONSUMABLE,
|
||||
}
|
||||
|
||||
// ResourceValue can handle both string and boolean values from the API
|
||||
type ResourceValue struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// ResourceLocation can handle both string and array values from the API
|
||||
type ResourceLocation struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// UnmarshalJSON customizes the JSON parsing method to handle string, boolean, and numeric values
|
||||
func (rv *ResourceValue) UnmarshalJSON(data []byte) error {
|
||||
// First try to parse as a string
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
rv.Value = str
|
||||
return nil
|
||||
}
|
||||
|
||||
// If it's not a string, try to parse as a boolean
|
||||
var boolVal bool
|
||||
if err := json.Unmarshal(data, &boolVal); err == nil {
|
||||
if boolVal {
|
||||
rv.Value = "true"
|
||||
} else {
|
||||
rv.Value = "false"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If it's not a boolean, try to parse as a number (int or float)
|
||||
var num json.Number
|
||||
if err := json.Unmarshal(data, &num); err == nil {
|
||||
rv.Value = num.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot unmarshal ResourceValue %s", string(data))
|
||||
}
|
||||
|
||||
// String returns the string representation of ResourceValue
|
||||
func (rv ResourceValue) String() string {
|
||||
return rv.Value
|
||||
}
|
||||
|
||||
// UnmarshalJSON customizes the JSON parsing method to handle string and array values
|
||||
func (rl *ResourceLocation) UnmarshalJSON(data []byte) error {
|
||||
// First try to parse as a string
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
rl.Value = str
|
||||
return nil
|
||||
}
|
||||
|
||||
// If it's not a string, try to parse as an array
|
||||
var arr []interface{}
|
||||
if err := json.Unmarshal(data, &arr); err == nil {
|
||||
// Convert array to comma-separated string
|
||||
var strValues []string
|
||||
for _, item := range arr {
|
||||
if strItem, ok := item.(string); ok {
|
||||
strValues = append(strValues, strItem)
|
||||
} else {
|
||||
// Convert non-string items to string
|
||||
jsonBytes, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot marshal location array item: %v", err)
|
||||
}
|
||||
strValues = append(strValues, string(jsonBytes))
|
||||
}
|
||||
}
|
||||
rl.Value = strings.Join(strValues, ", ")
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot unmarshal ResourceLocation %s", string(data))
|
||||
}
|
||||
|
||||
// String returns the string representation of ResourceLocation
|
||||
func (rl ResourceLocation) String() string {
|
||||
return rl.Value
|
||||
}
|
||||
|
||||
type ResourceDistResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []ResourceDist `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ResourceDist struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
ResourceValue ResourceValue `json:"value"`
|
||||
ResourceLoc ResourceLocation `json:"location"`
|
||||
}
|
||||
|
||||
func (r *ResourceDist) UnmarshalJSON(data []byte) error {
|
||||
type Alias ResourceDist
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = ResourceDist(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResourceDefResponse represents the API response for resource definition queries
|
||||
type ResourceDefResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []ResourceDef `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ResourceDef represents a resource definition
|
||||
type ResourceDef struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
Description string `json:"description"`
|
||||
ResourceType string `json:"-"` // populated from value_type int
|
||||
OrderType string `json:"-"` // populated from order_type int
|
||||
Flag map[string]interface{} `json:"flag"`
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
||||
func (r *ResourceDef) UnmarshalJSON(data []byte) error {
|
||||
type Alias struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
Description string `json:"description"`
|
||||
ResourceType string `json:"value_type"`
|
||||
OrderType string `json:"order_type"`
|
||||
Flag map[string]interface{} `json:"flag"`
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
r.ResourceName = a.ResourceName
|
||||
r.Description = a.Description
|
||||
r.ResourceType = a.ResourceType
|
||||
r.OrderType = a.OrderType
|
||||
r.Flag = a.Flag
|
||||
r.Interval = a.Interval
|
||||
return nil
|
||||
}
|
||||
150
src/frontend/xcecli/models/response_test.go
Normal file
150
src/frontend/xcecli/models/response_test.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Wire format from actual server responses (OrderMap wrapper with string status fields)
|
||||
|
||||
func TestHostsResponse_FullUnmarshal(t *testing.T) {
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["host_name","host_status","num_cpus","max_slot","num_run","num_ssusp","num_ususp","host_type"],"Data":{"host_name":"host1","host_status":"OK","num_cpus":16,"max_slot":16,"num_run":3,"num_ssusp":0,"num_ususp":0,"host_type":"linux"}}],"page_token":""}}`
|
||||
var resp HostsResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
h := resp.Data.Rows[0]
|
||||
if h.HostName != "host1" {
|
||||
t.Errorf("HostName: got %q want host1", h.HostName)
|
||||
}
|
||||
if h.HostStatus != "OK" {
|
||||
t.Errorf("HostStatus: got %q want OK", h.HostStatus)
|
||||
}
|
||||
if h.NumCPUs != 16 {
|
||||
t.Errorf("NumCPUs: got %d want 16", h.NumCPUs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueuesResponse_FullUnmarshal(t *testing.T) {
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["queue_name","queue_status","priority","num_pend","num_run"],"Data":{"queue_name":"normal","queue_status":"open:active","priority":30,"num_pend":5,"num_run":3}}],"page_token":""}}`
|
||||
var resp QueuesResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
q := resp.Data.Rows[0]
|
||||
if q.QueueName != "normal" {
|
||||
t.Errorf("QueueName: got %q want normal", q.QueueName)
|
||||
}
|
||||
if q.QueueStatus != "open:active" {
|
||||
t.Errorf("QueueStatus: got %q want open:active", q.QueueStatus)
|
||||
}
|
||||
if q.NumPend != 5 {
|
||||
t.Errorf("NumPend: got %d want 5", q.NumPend)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsResponse_FullUnmarshal(t *testing.T) {
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["job_id","submit_user","job_status","queue_name","job_name"],"Data":{"job_id":1001,"submit_user":"user1","job_status":"RUN","queue_name":"normal","job_name":"test_job_1"}}],"page_token":""}}`
|
||||
var resp JobsResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
j := resp.Data.Rows[0]
|
||||
if j.JobID != 1001 {
|
||||
t.Errorf("JobID: got %d want 1001", j.JobID)
|
||||
}
|
||||
if j.JobStatus != "RUN" {
|
||||
t.Errorf("JobStatus: got %q want RUN", j.JobStatus)
|
||||
}
|
||||
if j.SubmitUser != "user1" {
|
||||
t.Errorf("SubmitUser: got %q want user1", j.SubmitUser)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRolesResponse_FullUnmarshal(t *testing.T) {
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["role_name","action","domain_type","domain_values"],"Data":{"role_name":"basic_user","action":"cluster:view","domain_type":"all","domain_values":null}}],"page_token":""}}`
|
||||
var resp RolesResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
r := resp.Data.Rows[0]
|
||||
if r.RoleName != "basic_user" {
|
||||
t.Errorf("RoleName: got %q want basic_user", r.RoleName)
|
||||
}
|
||||
if r.PermName != "cluster:view" {
|
||||
t.Errorf("PermName: got %q want cluster:view", r.PermName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRolesResponse_FullUnmarshal(t *testing.T) {
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["user_name","role_name"],"Data":{"user_name":"admin","role_name":"cluster_admin"}}],"page_token":""}}`
|
||||
var resp UserRolesResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
u := resp.Data.Rows[0]
|
||||
if u.UserName != "admin" {
|
||||
t.Errorf("UserName: got %q want admin", u.UserName)
|
||||
}
|
||||
if u.UserRoleName != "cluster_admin" {
|
||||
t.Errorf("UserRoleName: got %q want cluster_admin", u.UserRoleName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDistResponse_FullUnmarshal(t *testing.T) {
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["res_name","value","location","is_dynamic"],"Data":{"res_name":"ncpus","value":16,"location":"host1","is_dynamic":false}}],"page_token":""}}`
|
||||
var resp ResourceDistResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
r := resp.Data.Rows[0]
|
||||
if r.ResourceName != "ncpus" {
|
||||
t.Errorf("ResourceName: got %q want ncpus", r.ResourceName)
|
||||
}
|
||||
if r.ResourceValue.String() != "16" {
|
||||
t.Errorf("ResourceValue: got %q want 16", r.ResourceValue.String())
|
||||
}
|
||||
if r.ResourceLoc.String() != "host1" {
|
||||
t.Errorf("ResourceLoc: got %q want host1", r.ResourceLoc.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceDefResponse_FullUnmarshal(t *testing.T) {
|
||||
// server sends value_type and order_type as strings, flag as object
|
||||
raw := `{"code":200,"msg":"Success","data":{"count":1,"rows":[{"Fields":["res_name","description","value_type","order_type","flag","interval"],"Data":{"res_name":"gpu","description":"GPU devices","value_type":"Numeric","order_type":"Decr","flag":{"builtin":true,"consumable":true,"dynamic":true,"release":false},"interval":60}}],"page_token":""}}`
|
||||
var resp ResourceDefResponse
|
||||
if err := json.Unmarshal([]byte(raw), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Data.Rows) != 1 {
|
||||
t.Fatalf("expected 1 row, got %d", len(resp.Data.Rows))
|
||||
}
|
||||
r := resp.Data.Rows[0]
|
||||
if r.ResourceName != "gpu" {
|
||||
t.Errorf("ResourceName: got %q want gpu", r.ResourceName)
|
||||
}
|
||||
if r.ResourceType != "Numeric" {
|
||||
t.Errorf("ResourceType: got %q want Numeric", r.ResourceType)
|
||||
}
|
||||
if r.OrderType != "Decr" {
|
||||
t.Errorf("OrderType: got %q want Decr", r.OrderType)
|
||||
}
|
||||
}
|
||||
53
src/frontend/xcecli/models/roles.go
Normal file
53
src/frontend/xcecli/models/roles.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package models
|
||||
|
||||
type CreateRoleRequest struct {
|
||||
RoleName string `json:"role_name" binding:"required"`
|
||||
Permission []Permission `json:"permissions" binding:"required"`
|
||||
}
|
||||
|
||||
type CreateRoleResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
RoleName string `json:"role_name"`
|
||||
Message string `json:"message"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type RoleInfo struct {
|
||||
RoleName string `json:"role_name"`
|
||||
PermName string `json:"action"`
|
||||
DomainType string `json:"domain_type"`
|
||||
DomainValues interface{} `json:"domain_values"`
|
||||
}
|
||||
|
||||
type RolesResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []RoleInfo `json:"rows"`
|
||||
PageToken string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
const (
|
||||
HEADER_ROLE_NAME = "ROLE_NAME"
|
||||
HEADER_ROLE_PERM_NAME = "PERM_NAME"
|
||||
HEADER_ROLE_DOMAIN_TYPE = "DOMAIN_TYPE"
|
||||
HEADER_ROLE_DOMAIN_VALUES = "DOMAIN_VALUES"
|
||||
)
|
||||
|
||||
var RoleFieldsMap = map[string]string{
|
||||
HEADER_ROLE_NAME: "role_name",
|
||||
}
|
||||
|
||||
func (r *RoleInfo) UnmarshalJSON(data []byte) error {
|
||||
type Alias RoleInfo
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = RoleInfo(a)
|
||||
return nil
|
||||
}
|
||||
54
src/frontend/xcecli/models/users.go
Normal file
54
src/frontend/xcecli/models/users.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
HEADER_USER_NAME = "USER_NAME"
|
||||
HEADER_USERROLE_NAME = "ROLE_NAME"
|
||||
)
|
||||
|
||||
var UserRoleFieldsMap = map[string]string{
|
||||
HEADER_USER_NAME: "user_name",
|
||||
}
|
||||
|
||||
type AddRoleForUserRequest struct {
|
||||
UserName string `json:"user_name" binding:"required"`
|
||||
RoleName []string `json:"role_name" binding:"required"`
|
||||
}
|
||||
|
||||
type UserAssignRoleResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
UserName string `json:"user_name"`
|
||||
Message string `json:"message"`
|
||||
} `json:"data"`
|
||||
}
|
||||
type UserRolesResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []UserRoleInfo `json:"rows"`
|
||||
PageToken string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type UserRoleInfo struct {
|
||||
UserName string `json:"user_name"`
|
||||
UserRoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
func (u *UserRoleInfo) UnmarshalJSON(data []byte) error {
|
||||
type Alias UserRoleInfo
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*u = UserRoleInfo(a)
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue