Skip to content

Commit eea1780

Browse files
authored
Add SchedulerConfig; fix reporting_params; apptainer in container (#11)
* Cluster Simulator. Improved error messages for qconf * Updated README.md * Adding scheduler config type; fix reporting_params; apptainer
1 parent a119b68 commit eea1780

File tree

6 files changed

+352
-16
lines changed

6 files changed

+352
-16
lines changed

cluster.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,18 @@
713713
"hostlist": ["master"]
714714
}
715715
],
716-
"resource_quota_sets": null,
716+
"resource_quota_sets": [
717+
{
718+
"name": "test_q_limit",
719+
"description": "This is a very detailed description",
720+
"enabled": false,
721+
"limits": [
722+
"users @allusers queues all.q to slots=2000",
723+
"users @arusers queues all.q to slots=1331",
724+
"users {@allusers} queues all.q to slots=333"
725+
]
726+
}
727+
],
717728
"managers": ["root"],
718729
"operators": [],
719730
"parallel_environments": [

pkg/qconf/compare_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ var _ = Describe("CompareTo", func() {
112112
// 1 if root submitted a job already
113113
Expect(len(comparison.DiffAdded.Users)).To(BeNumerically(">=", 0))
114114
Expect(len(comparison.DiffAdded.Managers)).To(BeNumerically("==", 1))
115-
Expect(len(comparison.DiffAdded.Operators)).To(BeNumerically("==", 0))
115+
Expect(len(comparison.DiffAdded.Operators)).To(BeNumerically("==", 1))
116116

117117
Expect(comparison.DiffModified).NotTo(BeNil())
118118
Expect(comparison.DiffModified.GlobalConfig).To(Equal(cc.GlobalConfig))

pkg/qconf/helper.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,20 @@ func ParseSpaceSeparatedMultiLineValues(lines []string, i int) []string {
7474
return entries
7575
}
7676

77+
// ParseSpaceAndCommaSeparatedMultiLineValues splits on spaces and commas.
78+
func ParseSpaceAndCommaSeparatedMultiLineValues(lines []string, i int) []string {
79+
vals, _ := ParseMultiLineValue(lines, i)
80+
81+
// Split on spaces and commas
82+
entries := strings.FieldsFunc(vals, func(r rune) bool {
83+
return r == ' ' || r == ','
84+
})
85+
if len(entries) == 1 && strings.ToUpper(entries[0]) == "NONE" {
86+
return nil
87+
}
88+
return entries
89+
}
90+
7791
// parseMultiLineValue parses a multi-line value from the output.
7892
// This is tricky because the output is not structured and the values can be
7993
// split over multiple lines.

pkg/qconf/qconf_impl.go

Lines changed: 191 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -797,11 +797,11 @@ func (c *CommandLineQConf) ShowGlobalConfiguration() (GlobalConfig, error) {
797797
case "shepherd_cmd":
798798
cfg.ShepherdCmd = fields[1]
799799
case "qmaster_params":
800-
cfg.QmasterParams = ParseCommaSeparatedMultiLineValues(lines, i)
800+
cfg.QmasterParams = ParseSpaceAndCommaSeparatedMultiLineValues(lines, i)
801801
case "execd_params":
802-
cfg.ExecdParams = ParseCommaSeparatedMultiLineValues(lines, i)
802+
cfg.ExecdParams = ParseSpaceAndCommaSeparatedMultiLineValues(lines, i)
803803
case "reporting_params":
804-
cfg.ReportingParams = ParseCommaSeparatedMultiLineValues(lines, i)
804+
cfg.ReportingParams = ParseSpaceAndCommaSeparatedMultiLineValues(lines, i)
805805
case "finished_jobs":
806806
cfg.FinishedJobs, _ = strconv.Atoi(fields[1])
807807
case "gid_range":
@@ -2069,7 +2069,7 @@ func (c *CommandLineQConf) ShowUserSetList(listnameList string) (UserSetListConf
20692069
case "oticket":
20702070
cfg.OTicket, _ = strconv.Atoi(fields[1])
20712071
case "entries":
2072-
cfg.Entries = ParseSpaceSeparatedMultiLineValues(lines, i)
2072+
cfg.Entries = ParseCommaSeparatedMultiLineValues(lines, i)
20732073
}
20742074
}
20752075
return cfg, nil
@@ -2441,9 +2441,9 @@ func (c *CommandLineQConf) ModifyGlobalConfig(cfg GlobalConfig) error {
24412441
}
24422442
switch fieldName {
24432443
case "complex_values", "login_shells", "qmaster_params",
2444-
"execd_params", "reporting_params", "load_sensor", "gid_range", "jsv_allowed_mod":
2444+
"execd_params", "load_sensor", "gid_range", "jsv_allowed_mod":
24452445
fieldValue = strings.Join(fieldValue.([]string), ",")
2446-
case "user_lists", "xuser_lists", "projects", "xprojects":
2446+
case "user_lists", "xuser_lists", "projects", "xprojects", "reporting_params":
24472447
fieldValue = strings.Join(fieldValue.([]string), " ")
24482448
default:
24492449
return fmt.Errorf("unsupported slice type: %s", fieldName)
@@ -2947,3 +2947,188 @@ func (c *CommandLineQConf) DeleteAttribute(objName, attrName, val, objIDList str
29472947
_, err := c.RunCommand("-dattr", objName, attrName, val, objIDList)
29482948
return err
29492949
}
2950+
2951+
// ShowSchedulerConfiguration shows the scheduler configuration.
2952+
func (c *CommandLineQConf) ShowSchedulerConfiguration() (SchedulerConfig, error) {
2953+
out, err := c.RunCommand("-ssconf")
2954+
if err != nil {
2955+
return SchedulerConfig{}, err
2956+
}
2957+
lines := strings.Split(out, "\n")
2958+
cfg := SchedulerConfig{}
2959+
for i, line := range lines {
2960+
fields := strings.Fields(line)
2961+
if len(fields) < 2 {
2962+
continue
2963+
}
2964+
value := fields[1]
2965+
2966+
switch fields[0] {
2967+
case "algorithm":
2968+
cfg.Algorithm = value
2969+
case "schedule_interval":
2970+
cfg.ScheduleInterval = value
2971+
case "maxujobs":
2972+
cfg.MaxUJobs, _ = strconv.Atoi(value)
2973+
case "queue_sort_method":
2974+
cfg.QueueSortMethod = value
2975+
case "job_load_adjustments":
2976+
cfg.JobLoadAdjustments = ParseCommaSeparatedMultiLineValues(lines, i)
2977+
case "load_adjustment_decay_time":
2978+
cfg.LoadAdjustmentDecayTime = value
2979+
case "load_formula":
2980+
cfg.LoadFormula = value
2981+
case "schedd_job_info":
2982+
cfg.ScheddJobInfo = value
2983+
case "flush_submit_sec":
2984+
cfg.FlushSubmitSec, _ = strconv.Atoi(value)
2985+
case "flush_finish_sec":
2986+
cfg.FlushFinishSec, _ = strconv.Atoi(value)
2987+
case "params":
2988+
cfg.Params = ParseSpaceAndCommaSeparatedMultiLineValues(lines, i)
2989+
case "reprioritize_interval":
2990+
cfg.ReprioritizeInterval = value
2991+
case "halftime":
2992+
cfg.Halftime, _ = strconv.Atoi(value)
2993+
case "usage_weight_list":
2994+
cfg.UsageWeightList = ParseCommaSeparatedMultiLineValues(lines, i)
2995+
case "compensation_factor":
2996+
cfg.CompensationFactor, _ = strconv.ParseFloat(value, 64)
2997+
case "weight_user":
2998+
cfg.WeightUser, _ = strconv.ParseFloat(value, 64)
2999+
case "weight_project":
3000+
cfg.WeightProject, _ = strconv.ParseFloat(value, 64)
3001+
case "weight_department":
3002+
cfg.WeightDepartment, _ = strconv.ParseFloat(value, 64)
3003+
case "weight_job":
3004+
cfg.WeightJob, _ = strconv.ParseFloat(value, 64)
3005+
case "weight_tickets_functional":
3006+
cfg.WeightTicketsFunctional, _ = strconv.Atoi(value)
3007+
case "weight_tickets_share":
3008+
cfg.WeightTicketsShare, _ = strconv.Atoi(value)
3009+
case "share_override_tickets":
3010+
cfg.ShareOverrideTickets, _ = strconv.ParseBool(value)
3011+
case "share_functional_shares":
3012+
cfg.ShareFunctionalShares, _ = strconv.ParseBool(value)
3013+
case "max_functional_jobs_to_schedule":
3014+
cfg.MaxFunctionalJobsToSchedule, _ = strconv.Atoi(value)
3015+
case "report_pjob_tickets":
3016+
cfg.ReportPJobTickets, _ = strconv.ParseBool(value)
3017+
case "max_pending_tasks_per_job":
3018+
cfg.MaxPendingTasksPerJob, _ = strconv.Atoi(value)
3019+
case "halflife_decay_list":
3020+
cfg.HalflifeDecayList = ParseCommaSeparatedMultiLineValues(lines, i)
3021+
case "policy_hierarchy":
3022+
cfg.PolicyHierarchy = value
3023+
case "weight_ticket":
3024+
cfg.WeightTicket, _ = strconv.ParseFloat(value, 64)
3025+
case "weight_waiting_time":
3026+
cfg.WeightWaitingTime, _ = strconv.ParseFloat(value, 64)
3027+
case "weight_deadline":
3028+
cfg.WeightDeadline, _ = strconv.ParseFloat(value, 64)
3029+
case "weight_urgency":
3030+
cfg.WeightUrgency, _ = strconv.ParseFloat(value, 64)
3031+
case "weight_priority":
3032+
cfg.WeightPriority, _ = strconv.ParseFloat(value, 64)
3033+
case "max_reservation":
3034+
cfg.MaxReservation, _ = strconv.Atoi(value)
3035+
case "default_duration":
3036+
cfg.DefaultDuration = value
3037+
}
3038+
}
3039+
return cfg, nil
3040+
}
3041+
3042+
// ModifySchedulerConfig modifies the scheduler configuration.
3043+
func (c *CommandLineQConf) ModifySchedulerConfig(cfg SchedulerConfig) error {
3044+
3045+
// set defaults
3046+
if cfg.LoadAdjustmentDecayTime == "" {
3047+
cfg.LoadAdjustmentDecayTime = "00:00:00"
3048+
}
3049+
3050+
if cfg.ScheduleInterval == "" {
3051+
cfg.ScheduleInterval = "00:00:00"
3052+
}
3053+
3054+
if cfg.ScheddJobInfo == "" {
3055+
cfg.ScheddJobInfo = "false"
3056+
}
3057+
3058+
if cfg.DefaultDuration == "" {
3059+
cfg.DefaultDuration = "00:00:00"
3060+
}
3061+
3062+
if cfg.ReprioritizeInterval == "" {
3063+
cfg.ReprioritizeInterval = "00:00:00"
3064+
}
3065+
3066+
if cfg.MaxPendingTasksPerJob == 0 {
3067+
// must be > 0
3068+
cfg.MaxPendingTasksPerJob = 50
3069+
}
3070+
3071+
if cfg.Algorithm == "" {
3072+
cfg.Algorithm = "default"
3073+
}
3074+
3075+
if cfg.LoadFormula == "" {
3076+
cfg.LoadFormula = "np_load_avg"
3077+
}
3078+
3079+
file, err := createTempDirWithFileName("scheduler")
3080+
if err != nil {
3081+
return err
3082+
}
3083+
//defer os.RemoveAll(filepath.Dir(file.Name()))
3084+
3085+
v := reflect.ValueOf(cfg)
3086+
typeOfS := v.Type()
3087+
for i := 0; i < v.NumField(); i++ {
3088+
fieldName := typeOfS.Field(i).Tag.Get("json")
3089+
fieldValue := v.Field(i).Interface()
3090+
// if type is []string, join the values either
3091+
// comma separated or space separated depending on fieldName
3092+
if reflect.TypeOf(fieldValue).Kind() == reflect.Slice {
3093+
if len(fieldValue.([]string)) == 0 {
3094+
fieldValue = []string{"NONE"}
3095+
}
3096+
switch fieldName {
3097+
case "job_load_adjustments", "halflife_decay_list", "usage_weight_list", "params":
3098+
fieldValue = strings.Join(fieldValue.([]string), ",")
3099+
default:
3100+
return fmt.Errorf("unsupported slice type: %s", fieldName)
3101+
}
3102+
}
3103+
3104+
// for booleans, write "TRUE" or "FALSE"
3105+
if reflect.TypeOf(fieldValue).Kind() == reflect.Bool {
3106+
if fieldValue.(bool) {
3107+
fieldValue = "TRUE"
3108+
} else {
3109+
fieldValue = "FALSE"
3110+
}
3111+
}
3112+
3113+
// for float64, write the value with 6 decimal places
3114+
if reflect.TypeOf(fieldValue).Kind() == reflect.Float64 {
3115+
fieldValue = fmt.Sprintf("%.6f", fieldValue)
3116+
}
3117+
3118+
// an empty string should be written as "NONE"
3119+
if reflect.TypeOf(fieldValue).Kind() == reflect.String {
3120+
if fieldValue.(string) == "" {
3121+
fieldValue = "NONE"
3122+
}
3123+
}
3124+
3125+
_, err = file.WriteString(fmt.Sprintf("%s %v\n", fieldName, fieldValue))
3126+
if err != nil {
3127+
return err
3128+
}
3129+
}
3130+
file.Close()
3131+
3132+
_, err = c.RunCommand("-Msconf", file.Name())
3133+
return err
3134+
}

0 commit comments

Comments
 (0)