├── .gitignore ├── .travis.yml ├── .setup.sh ├── create-cert.sh ├── LICENSE ├── README.md └── slurm-https.go /.gitignore: -------------------------------------------------------------------------------- 1 | slurm 2 | slurm-https 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: go 4 | 5 | env: 6 | - PKG_CONFIG_PATH=/tmp/lib/pkgconfig 7 | 8 | install: 9 | - sh .setup.sh 10 | 11 | script: 12 | - go build 13 | -------------------------------------------------------------------------------- /.setup.sh: -------------------------------------------------------------------------------- 1 | git clone https://github.com/SchedMD/slurm 2 | cd slurm 3 | 4 | prefix=/tmp 5 | 6 | ./autogen.sh 7 | ./configure --disable-dependency-tracking --prefix=$prefix 8 | make install -j 9 | 10 | [ "$PKG_CONFIG_PATH" ] || PKG_CONFIG_PATH=$prefix/lib/pkgconfig 11 | 12 | mkdir -p ${PKG_CONFIG_PATH} 13 | cat >${PKG_CONFIG_PATH}/slurm.pc <openssl.conf<ca.srl< 7 | #include 8 | #include 9 | 10 | #include "slurm/slurm.h" 11 | #include "slurm/slurm_errno.h" 12 | 13 | typedef char* chars; 14 | 15 | #define SLUW_LIST(T,S) \ 16 | T *sluw_alloc_##T (int s) { T *r = (T *)calloc(s+1, sizeof(T)); if (r) r[s]=S; return r;} \ 17 | void sluw_set_##T (T *l, T v, int p) { l[p]=v; } \ 18 | size_t sluw_len_##T (T *l) { size_t i=0; if (l) while (l[i]!=S) i++; return i;} 19 | 20 | SLUW_LIST(uint32_t,0) 21 | SLUW_LIST(int32_t,-1) 22 | SLUW_LIST(chars,NULL) 23 | */ 24 | import ( 25 | "C" 26 | ) 27 | 28 | import ( 29 | "crypto/tls" 30 | "crypto/x509" 31 | "encoding/json" 32 | "flag" 33 | "io/ioutil" 34 | "log" 35 | "net/http" 36 | "reflect" 37 | "strconv" 38 | "strings" 39 | "unsafe" 40 | ) 41 | 42 | func slurm_error(w http.ResponseWriter, r *http.Request) { 43 | errno := C.slurm_get_errno() 44 | errno_str := "SLURM-" + strconv.Itoa(int(errno)) + " " + C.GoString(C.slurm_strerror(errno)) 45 | log.Println("from:", r.RemoteAddr, "request:", r.RequestURI, errno_str) 46 | http.Error(w, errno_str, 500) 47 | } 48 | 49 | func sluw_get_name(s string) string { 50 | if len(s) < 2 { 51 | return strings.ToUpper(s) 52 | } 53 | 54 | str := strings.Split(s, "_") 55 | 56 | for i, v := range str { 57 | tmp := strings.SplitN(v, "", 2) 58 | tmp[0] = strings.ToUpper(tmp[0]) 59 | str[i] = strings.Join(tmp, "") 60 | } 61 | 62 | return strings.Join(str, "") 63 | } 64 | 65 | type object struct { 66 | Type string 67 | Offset unsafe.Pointer 68 | } 69 | 70 | type object_map map[string]object 71 | 72 | func (t object_map) Add(data interface{}) { 73 | val := reflect.ValueOf(data) 74 | 75 | if val.Kind() != reflect.Ptr { 76 | return 77 | } 78 | 79 | val = val.Elem() 80 | 81 | if !val.CanAddr() { 82 | return 83 | } 84 | 85 | ptr := val.Addr().Pointer() 86 | 87 | for i := 0; i < val.NumField(); i++ { 88 | name := val.Type().Field(i).Name 89 | offset := val.Type().Field(i).Offset 90 | field := val.Field(i) 91 | if name == "_" { 92 | continue 93 | } 94 | t[sluw_get_name(name)] = object{ 95 | field.Type().String(), 96 | unsafe.Pointer(ptr + offset), 97 | } 98 | } 99 | } 100 | 101 | func (t object_map) Run(w http.ResponseWriter, r *http.Request, fn func()) { 102 | var req map[string]*json.RawMessage 103 | err := json.NewDecoder(r.Body).Decode(&req) 104 | 105 | if err != nil { 106 | http.Error(w, "Bad request", 400) 107 | return 108 | } 109 | 110 | for key, value := range req { 111 | dst, ok := t[key] 112 | 113 | if !ok { 114 | http.Error(w, "Unknown key: "+key, 400) 115 | return 116 | } 117 | 118 | var err error 119 | 120 | switch dst.Type { 121 | case "*main._Ctype_char": 122 | var s string 123 | err = json.Unmarshal(*value, &s) 124 | tmp := C.CString(s) 125 | *(**C.char)(dst.Offset) = tmp 126 | defer C.free(unsafe.Pointer(tmp)) 127 | case "main._Ctype_uint32_t": 128 | var i uint32 129 | err = json.Unmarshal(*value, &i) 130 | *(*C.uint32_t)(dst.Offset) = C.uint32_t(i) 131 | case "main._Ctype_uint16_t": 132 | var i uint16 133 | err = json.Unmarshal(*value, &i) 134 | *(*C.uint16_t)(dst.Offset) = C.uint16_t(i) 135 | case "main._Ctype_uint8_t": 136 | var i uint8 137 | err = json.Unmarshal(*value, &i) 138 | *(*C.uint8_t)(dst.Offset) = C.uint8_t(i) 139 | case "main._Ctype_int32_t": 140 | var i int32 141 | err = json.Unmarshal(*value, &i) 142 | *(*C.int32_t)(dst.Offset) = C.int32_t(i) 143 | case "main._Ctype_int16_t": 144 | var i int16 145 | err = json.Unmarshal(*value, &i) 146 | *(*C.int16_t)(dst.Offset) = C.int16_t(i) 147 | case "main._Ctype_int8_t": 148 | var i int8 149 | err = json.Unmarshal(*value, &i) 150 | *(*C.int8_t)(dst.Offset) = C.int8_t(i) 151 | case "main._Ctype_time_t": 152 | var i uint64 153 | err = json.Unmarshal(*value, &i) 154 | *(*C.time_t)(dst.Offset) = C.time_t(i) 155 | case "*main._Ctype_uint32_t": 156 | var ai []uint32 157 | err = json.Unmarshal(*value, &ai) 158 | tmp := C.sluw_alloc_uint32_t(C.int(len(ai))) 159 | defer C.free(unsafe.Pointer(tmp)) 160 | for i := 0; i < len(ai); i++ { 161 | C.sluw_set_uint32_t(tmp, C.uint32_t(ai[i]), C.int(i)) 162 | } 163 | *(**C.uint32_t)(dst.Offset) = tmp 164 | case "*main._Ctype_int32_t": 165 | var ai []int32 166 | err = json.Unmarshal(*value, &ai) 167 | tmp := C.sluw_alloc_int32_t(C.int(len(ai))) 168 | defer C.free(unsafe.Pointer(tmp)) 169 | for i := 0; i < len(ai); i++ { 170 | C.sluw_set_int32_t(tmp, C.int32_t(ai[i]), C.int(i)) 171 | } 172 | *(**C.int32_t)(dst.Offset) = tmp 173 | case "**main._Ctype_char": 174 | var as []string 175 | err = json.Unmarshal(*value, &as) 176 | tmp := C.sluw_alloc_chars(C.int(len(as))) 177 | defer C.free(unsafe.Pointer(tmp)) 178 | for i := 0; i < len(as); i++ { 179 | tmp2 := C.CString(as[i]) 180 | defer C.free(unsafe.Pointer(tmp2)) 181 | C.sluw_set_chars(tmp, tmp2, C.int(i)) 182 | } 183 | *(***C.char)(dst.Offset) = (**C.char)(tmp) 184 | default: 185 | log.Println(key, reflect.TypeOf(dst), "not supported") 186 | } 187 | 188 | if err != nil { 189 | http.Error(w, "Bad value for key: "+key, 400) 190 | return 191 | } 192 | } 193 | 194 | fn() 195 | } 196 | 197 | type table map[string]interface{} 198 | 199 | func get_res(data interface{}) *table { 200 | ret := make(table) 201 | val := reflect.ValueOf(data).Elem() 202 | for i := 0; i < val.NumField(); i++ { 203 | f := val.Type().Field(i) 204 | if f.Name == "_" { 205 | continue 206 | } 207 | name := sluw_get_name(f.Name) 208 | v := val.Field(i) 209 | switch f.Type.String() { 210 | case "main._Ctype_uint8_t", 211 | "main._Ctype_uint16_t", 212 | "main._Ctype_uint32_t", 213 | "main._Ctype_uint64_t": 214 | ret[name] = uint(v.Uint()) 215 | case "main._Ctype_int8_t", 216 | "main._Ctype_int16_t", 217 | "main._Ctype_int32_t", 218 | "main._Ctype_int64_t", 219 | "main._Ctype_time_t": // why not.. 220 | ret[name] = int(v.Int()) 221 | case "*main._Ctype_uint32_t": 222 | if v.Pointer() == 0 { 223 | ret[name] = make([]uint, 0) 224 | break 225 | } 226 | data := unsafe.Pointer(v.Pointer()) 227 | count := int(C.sluw_len_uint32_t((*C.uint32_t)(data))) 228 | array := make([]uint, count) 229 | carray := *(*[]C.uint32_t)(unsafe.Pointer(&reflect.SliceHeader{ 230 | Data: uintptr(data), 231 | Len: count, 232 | Cap: count, 233 | })) 234 | for k := 0; k < count; k++ { 235 | array[k] = uint(carray[k]) 236 | } 237 | ret[name] = array 238 | case "*main._Ctype_int32_t": 239 | if v.Pointer() == 0 { 240 | ret[name] = make([]int, 0) 241 | break 242 | } 243 | data := unsafe.Pointer(v.Pointer()) 244 | count := int(C.sluw_len_int32_t((*C.int32_t)(data))) 245 | array := make([]int, count) 246 | carray := *(*[]C.int32_t)(unsafe.Pointer(&reflect.SliceHeader{ 247 | Data: uintptr(data), 248 | Len: count, 249 | Cap: count, 250 | })) 251 | for k := 0; k < count; k++ { 252 | array[k] = int(carray[k]) 253 | } 254 | ret[name] = array 255 | case "*main._Ctype_char": 256 | if v.Pointer() == 0 { 257 | ret[name] = nil 258 | break 259 | } 260 | ret[name] = C.GoString((*C.char)(unsafe.Pointer(v.Pointer()))) 261 | default: 262 | log.Println(name, f.Type, "not supported") 263 | } 264 | } 265 | 266 | return &ret 267 | } 268 | 269 | func submit_batch_job(w http.ResponseWriter, r *http.Request) { 270 | var slreq C.job_desc_msg_t 271 | C.slurm_init_job_desc_msg(&slreq) 272 | 273 | obj := make(object_map) 274 | obj.Add(&slreq) 275 | 276 | obj.Run(w, r, func() { 277 | var slres *C.submit_response_msg_t 278 | 279 | ret := C.slurm_submit_batch_job(&slreq, &slres) 280 | 281 | if ret != 0 { 282 | slurm_error(w, r) 283 | return 284 | } 285 | 286 | res := get_res(slres) 287 | C.slurm_free_submit_response_response_msg(slres) 288 | 289 | w.Header().Set("Content-Type", "application/json") 290 | json.NewEncoder(w).Encode(&res) 291 | }) 292 | } 293 | 294 | func notify_job(w http.ResponseWriter, r *http.Request) { 295 | opt := struct { 296 | job_id C.uint32_t 297 | message *C.char 298 | }{} 299 | 300 | obj := make(object_map) 301 | obj.Add(&opt) 302 | 303 | obj.Run(w, r, func() { 304 | ret := C.slurm_notify_job(opt.job_id, opt.message) 305 | 306 | if ret != 0 { 307 | slurm_error(w, r) 308 | return 309 | } 310 | }) 311 | } 312 | 313 | func update_job(w http.ResponseWriter, r *http.Request) { 314 | var slreq C.job_desc_msg_t 315 | C.slurm_init_job_desc_msg(&slreq) 316 | 317 | obj := make(object_map) 318 | obj.Add(&slreq) 319 | 320 | obj.Run(w, r, func() { 321 | ret := C.slurm_update_job(&slreq) 322 | 323 | if ret != 0 { 324 | slurm_error(w, r) 325 | return 326 | } 327 | }) 328 | } 329 | 330 | func load_jobs(w http.ResponseWriter, r *http.Request) { 331 | opt := struct { 332 | update_time C.time_t 333 | show_flags C.uint16_t 334 | }{} 335 | 336 | obj := make(object_map) 337 | obj.Add(&opt) 338 | 339 | obj.Run(w, r, func() { 340 | var slres *C.job_info_msg_t 341 | 342 | ret := C.slurm_load_jobs(opt.update_time, &slres, opt.show_flags) 343 | 344 | if ret != 0 { 345 | slurm_error(w, r) 346 | return 347 | } 348 | 349 | data := unsafe.Pointer(slres.job_array) 350 | count := int(slres.record_count) 351 | carray := *(*[]C.job_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 352 | Data: uintptr(data), 353 | Len: count, 354 | Cap: count, 355 | })) 356 | 357 | res := get_res(slres) 358 | 359 | array := make([]*table, count) 360 | for i := 0; i < count; i++ { 361 | array[i] = get_res(&carray[i]) 362 | } 363 | 364 | (*res)["JobArray"] = array 365 | 366 | C.slurm_free_job_info_msg(slres) 367 | 368 | w.Header().Set("Content-Type", "application/json") 369 | json.NewEncoder(w).Encode(&res) 370 | }) 371 | } 372 | 373 | func load_node(w http.ResponseWriter, r *http.Request) { 374 | opt := struct { 375 | update_time C.time_t 376 | show_flags C.uint16_t 377 | }{} 378 | 379 | obj := make(object_map) 380 | obj.Add(&opt) 381 | 382 | obj.Run(w, r, func() { 383 | var slres *C.node_info_msg_t 384 | 385 | ret := C.slurm_load_node(opt.update_time, &slres, opt.show_flags) 386 | 387 | if ret != 0 { 388 | slurm_error(w, r) 389 | return 390 | } 391 | 392 | data := unsafe.Pointer(slres.node_array) 393 | count := int(slres.record_count) 394 | carray := *(*[]C.node_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 395 | Data: uintptr(data), 396 | Len: count, 397 | Cap: count, 398 | })) 399 | 400 | res := get_res(slres) 401 | array := make([]*table, count) 402 | 403 | for i := 0; i < count; i++ { 404 | array[i] = get_res(&carray[i]) 405 | } 406 | 407 | (*res)["NodeArray"] = array 408 | 409 | C.slurm_free_node_info_msg(slres) 410 | 411 | w.Header().Set("Content-Type", "application/json") 412 | json.NewEncoder(w).Encode(&res) 413 | }) 414 | } 415 | 416 | func update_node(w http.ResponseWriter, r *http.Request) { 417 | var slreq C.update_node_msg_t 418 | C.slurm_init_update_node_msg(&slreq) 419 | 420 | obj := make(object_map) 421 | obj.Add(&slreq) 422 | 423 | obj.Run(w, r, func() { 424 | ret := C.slurm_update_node(&slreq) 425 | 426 | if ret != 0 { 427 | slurm_error(w, r) 428 | return 429 | } 430 | }) 431 | } 432 | 433 | func signal_job(w http.ResponseWriter, r *http.Request) { 434 | opt := struct { 435 | job_id C.uint32_t 436 | signal C.uint16_t 437 | }{} 438 | 439 | obj := make(object_map) 440 | obj.Add(&opt) 441 | 442 | obj.Run(w, r, func() { 443 | ret := C.slurm_signal_job(opt.job_id, opt.signal) 444 | 445 | if ret != 0 { 446 | slurm_error(w, r) 447 | return 448 | } 449 | }) 450 | } 451 | 452 | func signal_job_step(w http.ResponseWriter, r *http.Request) { 453 | opt := struct { 454 | job_id C.uint32_t 455 | step_id C.uint32_t 456 | signal C.uint32_t 457 | }{} 458 | 459 | obj := make(object_map) 460 | obj.Add(&opt) 461 | 462 | obj.Run(w, r, func() { 463 | ret := C.slurm_signal_job_step(opt.job_id, opt.step_id, opt.signal) 464 | 465 | if ret != 0 { 466 | slurm_error(w, r) 467 | return 468 | } 469 | }) 470 | } 471 | 472 | func kill_job(w http.ResponseWriter, r *http.Request) { 473 | opt := struct { 474 | job_id C.uint32_t 475 | signal C.uint16_t 476 | batch_flag C.uint16_t 477 | }{} 478 | 479 | obj := make(object_map) 480 | obj.Add(&opt) 481 | 482 | obj.Run(w, r, func() { 483 | ret := C.slurm_kill_job(opt.job_id, opt.signal, opt.batch_flag) 484 | 485 | if ret != 0 { 486 | slurm_error(w, r) 487 | return 488 | } 489 | }) 490 | } 491 | 492 | func kill_job_step(w http.ResponseWriter, r *http.Request) { 493 | opt := struct { 494 | job_id C.uint32_t 495 | step_id C.uint32_t 496 | signal C.uint16_t 497 | }{} 498 | 499 | obj := make(object_map) 500 | obj.Add(&opt) 501 | 502 | obj.Run(w, r, func() { 503 | ret := C.slurm_kill_job_step(opt.job_id, opt.step_id, opt.signal) 504 | 505 | if ret != 0 { 506 | slurm_error(w, r) 507 | return 508 | } 509 | }) 510 | } 511 | 512 | func complete_job(w http.ResponseWriter, r *http.Request) { 513 | opt := struct { 514 | job_id C.uint32_t 515 | job_return_code C.uint32_t 516 | }{} 517 | 518 | obj := make(object_map) 519 | obj.Add(&opt) 520 | 521 | obj.Run(w, r, func() { 522 | ret := C.slurm_complete_job(opt.job_id, opt.job_return_code) 523 | 524 | if ret != 0 { 525 | slurm_error(w, r) 526 | return 527 | } 528 | }) 529 | } 530 | 531 | func terminate_job_step(w http.ResponseWriter, r *http.Request) { 532 | opt := struct { 533 | job_id C.uint32_t 534 | step_id C.uint32_t 535 | }{} 536 | 537 | obj := make(object_map) 538 | obj.Add(&opt) 539 | 540 | obj.Run(w, r, func() { 541 | ret := C.slurm_terminate_job_step(opt.job_id, opt.step_id) 542 | 543 | if ret != 0 { 544 | slurm_error(w, r) 545 | return 546 | } 547 | }) 548 | } 549 | 550 | func suspend_job(w http.ResponseWriter, r *http.Request) { 551 | opt := struct { 552 | job_id C.uint32_t 553 | }{} 554 | 555 | obj := make(object_map) 556 | obj.Add(&opt) 557 | 558 | obj.Run(w, r, func() { 559 | ret := C.slurm_suspend(opt.job_id) 560 | 561 | if ret != 0 { 562 | slurm_error(w, r) 563 | return 564 | } 565 | }) 566 | } 567 | 568 | func resume_job(w http.ResponseWriter, r *http.Request) { 569 | opt := struct { 570 | job_id C.uint32_t 571 | }{} 572 | 573 | obj := make(object_map) 574 | obj.Add(&opt) 575 | 576 | obj.Run(w, r, func() { 577 | ret := C.slurm_resume(opt.job_id) 578 | 579 | if ret != 0 { 580 | slurm_error(w, r) 581 | return 582 | } 583 | }) 584 | } 585 | 586 | func requeue_job(w http.ResponseWriter, r *http.Request) { 587 | opt := struct { 588 | job_id C.uint32_t 589 | state C.uint32_t 590 | }{} 591 | 592 | obj := make(object_map) 593 | obj.Add(&opt) 594 | 595 | obj.Run(w, r, func() { 596 | ret := C.slurm_requeue(opt.job_id, opt.state) 597 | 598 | if ret != 0 { 599 | slurm_error(w, r) 600 | return 601 | } 602 | }) 603 | } 604 | 605 | func load_licenses(w http.ResponseWriter, r *http.Request) { 606 | opt := struct { 607 | update_time C.time_t 608 | show_flags C.uint16_t 609 | }{} 610 | 611 | obj := make(object_map) 612 | obj.Add(&opt) 613 | 614 | obj.Run(w, r, func() { 615 | var slres *C.license_info_msg_t 616 | 617 | ret := C.slurm_load_licenses(opt.update_time, &slres, opt.show_flags) 618 | 619 | if ret != 0 { 620 | slurm_error(w, r) 621 | return 622 | } 623 | 624 | data := unsafe.Pointer(slres.lic_array) 625 | count := int(slres.num_lic) 626 | carray := *(*[]C.slurm_license_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 627 | Data: uintptr(data), 628 | Len: count, 629 | Cap: count, 630 | })) 631 | 632 | res := get_res(slres) 633 | array := make([]*table, count) 634 | 635 | for i := 0; i < count; i++ { 636 | array[i] = get_res(&carray[i]) 637 | } 638 | 639 | (*res)["LicArray"] = array 640 | 641 | C.slurm_free_license_info_msg(slres) 642 | 643 | w.Header().Set("Content-Type", "application/json") 644 | json.NewEncoder(w).Encode(&res) 645 | }) 646 | } 647 | 648 | func load_reservations(w http.ResponseWriter, r *http.Request) { 649 | opt := struct { 650 | update_time C.time_t 651 | }{} 652 | 653 | obj := make(object_map) 654 | obj.Add(&opt) 655 | 656 | obj.Run(w, r, func() { 657 | var slres *C.reserve_info_msg_t 658 | 659 | ret := C.slurm_load_reservations(opt.update_time, &slres) 660 | 661 | if ret != 0 { 662 | slurm_error(w, r) 663 | return 664 | } 665 | 666 | data := unsafe.Pointer(slres.reservation_array) 667 | count := int(slres.record_count) 668 | carray := *(*[]C.reserve_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 669 | Data: uintptr(data), 670 | Len: count, 671 | Cap: count, 672 | })) 673 | 674 | res := get_res(slres) 675 | array := make([]*table, count) 676 | 677 | for i := 0; i < count; i++ { 678 | array[i] = get_res(&carray[i]) 679 | } 680 | 681 | (*res)["ReservationArray"] = array 682 | 683 | C.slurm_free_reservation_info_msg(slres) 684 | 685 | w.Header().Set("Content-Type", "application/json") 686 | json.NewEncoder(w).Encode(&res) 687 | }) 688 | } 689 | 690 | func delete_reservation(w http.ResponseWriter, r *http.Request) { 691 | var slreq C.reservation_name_msg_t 692 | 693 | obj := make(object_map) 694 | obj.Add(&slreq) 695 | 696 | obj.Run(w, r, func() { 697 | ret := C.slurm_delete_reservation(&slreq) 698 | 699 | if ret != 0 { 700 | slurm_error(w, r) 701 | return 702 | } 703 | }) 704 | } 705 | 706 | func create_reservation(w http.ResponseWriter, r *http.Request) { 707 | var slreq C.resv_desc_msg_t 708 | C.slurm_init_resv_desc_msg(&slreq) 709 | 710 | obj := make(object_map) 711 | obj.Add(&slreq) 712 | 713 | obj.Run(w, r, func() { 714 | ret := C.slurm_create_reservation(&slreq) 715 | 716 | if ret == nil { 717 | slurm_error(w, r) 718 | return 719 | } 720 | 721 | res := get_res(&slreq) 722 | 723 | w.Header().Set("Content-Type", "application/json") 724 | json.NewEncoder(w).Encode(&res) 725 | }) 726 | } 727 | 728 | func update_reservation(w http.ResponseWriter, r *http.Request) { 729 | var slreq C.resv_desc_msg_t 730 | C.slurm_init_resv_desc_msg(&slreq) 731 | 732 | obj := make(object_map) 733 | obj.Add(&slreq) 734 | 735 | obj.Run(w, r, func() { 736 | ret := C.slurm_update_reservation(&slreq) 737 | 738 | if ret != 0 { 739 | slurm_error(w, r) 740 | return 741 | } 742 | }) 743 | } 744 | 745 | func get_triggers(w http.ResponseWriter, r *http.Request) { 746 | var slres *C.trigger_info_msg_t 747 | 748 | ret := C.slurm_get_triggers(&slres) 749 | 750 | if ret != 0 { 751 | slurm_error(w, r) 752 | return 753 | } 754 | 755 | data := unsafe.Pointer(slres.trigger_array) 756 | count := int(slres.record_count) 757 | carray := *(*[]C.trigger_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 758 | Data: uintptr(data), 759 | Len: count, 760 | Cap: count, 761 | })) 762 | 763 | res := get_res(slres) 764 | array := make([]*table, count) 765 | 766 | for i := 0; i < count; i++ { 767 | array[i] = get_res(&carray[i]) 768 | } 769 | 770 | (*res)["TriggerArray"] = array 771 | 772 | C.slurm_free_trigger_msg(slres) 773 | 774 | w.Header().Set("Content-Type", "application/json") 775 | json.NewEncoder(w).Encode(&res) 776 | } 777 | 778 | func set_trigger(w http.ResponseWriter, r *http.Request) { 779 | var slreq C.trigger_info_t 780 | C.slurm_init_trigger_msg(&slreq) 781 | 782 | obj := make(object_map) 783 | obj.Add(&slreq) 784 | 785 | obj.Run(w, r, func() { 786 | ret := C.slurm_set_trigger(&slreq) 787 | 788 | if ret != 0 { 789 | slurm_error(w, r) 790 | return 791 | } 792 | }) 793 | } 794 | 795 | func clear_trigger(w http.ResponseWriter, r *http.Request) { 796 | var slreq C.trigger_info_t 797 | C.slurm_init_trigger_msg(&slreq) 798 | 799 | obj := make(object_map) 800 | obj.Add(&slreq) 801 | 802 | obj.Run(w, r, func() { 803 | ret := C.slurm_clear_trigger(&slreq) 804 | 805 | if ret != 0 { 806 | slurm_error(w, r) 807 | return 808 | } 809 | }) 810 | } 811 | 812 | func takeover(w http.ResponseWriter, r *http.Request) { 813 | opt := struct { 814 | backup_inx C.int 815 | }{} 816 | 817 | obj := make(object_map) 818 | obj.Add(&opt) 819 | 820 | obj.Run(w, r, func() { 821 | ret := C.slurm_takeover(opt.backup_inx) 822 | 823 | if ret != 0 { 824 | slurm_error(w, r) 825 | return 826 | } 827 | }) 828 | } 829 | 830 | func shutdown(w http.ResponseWriter, r *http.Request) { 831 | opt := struct { 832 | options C.uint16_t 833 | }{} 834 | 835 | obj := make(object_map) 836 | obj.Add(&opt) 837 | 838 | obj.Run(w, r, func() { 839 | ret := C.slurm_shutdown(opt.options) 840 | 841 | if ret != 0 { 842 | slurm_error(w, r) 843 | return 844 | } 845 | }) 846 | } 847 | 848 | func reconfigure(w http.ResponseWriter, r *http.Request) { 849 | ret := C.slurm_reconfigure() 850 | 851 | if ret != 0 { 852 | slurm_error(w, r) 853 | return 854 | } 855 | } 856 | 857 | func ping(w http.ResponseWriter, r *http.Request) { 858 | opt := struct { 859 | primary C.int 860 | }{} 861 | 862 | obj := make(object_map) 863 | obj.Add(&opt) 864 | 865 | obj.Run(w, r, func() { 866 | ret := C.slurm_ping(opt.primary) 867 | 868 | if ret != 0 { 869 | slurm_error(w, r) 870 | return 871 | } 872 | }) 873 | } 874 | 875 | func load_partitions(w http.ResponseWriter, r *http.Request) { 876 | opt := struct { 877 | update_time C.time_t 878 | show_flags C.uint16_t 879 | }{} 880 | 881 | obj := make(object_map) 882 | obj.Add(&opt) 883 | 884 | obj.Run(w, r, func() { 885 | var slres *C.partition_info_msg_t 886 | 887 | ret := C.slurm_load_partitions(opt.update_time, &slres, opt.show_flags) 888 | 889 | if ret != 0 { 890 | slurm_error(w, r) 891 | return 892 | } 893 | 894 | data := unsafe.Pointer(slres.partition_array) 895 | count := int(slres.record_count) 896 | carray := *(*[]C.partition_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 897 | Data: uintptr(data), 898 | Len: count, 899 | Cap: count, 900 | })) 901 | 902 | res := get_res(slres) 903 | array := make([]*table, count) 904 | 905 | for i := 0; i < count; i++ { 906 | array[i] = get_res(&carray[i]) 907 | } 908 | 909 | (*res)["PartitionArray"] = array 910 | 911 | C.slurm_free_partition_info_msg(slres) 912 | 913 | w.Header().Set("Content-Type", "application/json") 914 | json.NewEncoder(w).Encode(&res) 915 | }) 916 | } 917 | 918 | func create_partition(w http.ResponseWriter, r *http.Request) { 919 | var slreq C.update_part_msg_t 920 | C.slurm_init_part_desc_msg(&slreq) 921 | 922 | obj := make(object_map) 923 | obj.Add(&slreq) 924 | 925 | obj.Run(w, r, func() { 926 | ret := C.slurm_create_partition(&slreq) 927 | 928 | if ret != 0 { 929 | slurm_error(w, r) 930 | return 931 | } 932 | }) 933 | } 934 | 935 | func update_partition(w http.ResponseWriter, r *http.Request) { 936 | var slreq C.update_part_msg_t 937 | C.slurm_init_part_desc_msg(&slreq) 938 | 939 | obj := make(object_map) 940 | obj.Add(&slreq) 941 | 942 | obj.Run(w, r, func() { 943 | ret := C.slurm_update_partition(&slreq) 944 | 945 | if ret != 0 { 946 | slurm_error(w, r) 947 | return 948 | } 949 | }) 950 | } 951 | 952 | func delete_partition(w http.ResponseWriter, r *http.Request) { 953 | var slreq C.delete_part_msg_t 954 | 955 | obj := make(object_map) 956 | obj.Add(&slreq) 957 | 958 | obj.Run(w, r, func() { 959 | ret := C.slurm_delete_partition(&slreq) 960 | 961 | if ret != 0 { 962 | slurm_error(w, r) 963 | return 964 | } 965 | }) 966 | } 967 | 968 | func load_topo(w http.ResponseWriter, r *http.Request) { 969 | var slres *C.topo_info_response_msg_t 970 | 971 | ret := C.slurm_load_topo(&slres) 972 | 973 | if ret != 0 { 974 | slurm_error(w, r) 975 | return 976 | } 977 | 978 | data := unsafe.Pointer(slres.topo_array) 979 | count := int(slres.record_count) 980 | carray := *(*[]C.topo_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 981 | Data: uintptr(data), 982 | Len: count, 983 | Cap: count, 984 | })) 985 | 986 | res := get_res(slres) 987 | array := make([]*table, count) 988 | 989 | for i := 0; i < count; i++ { 990 | array[i] = get_res(&carray[i]) 991 | } 992 | 993 | (*res)["TopoArray"] = array 994 | 995 | C.slurm_free_topo_info_msg(slres) 996 | 997 | w.Header().Set("Content-Type", "application/json") 998 | json.NewEncoder(w).Encode(&res) 999 | } 1000 | 1001 | func load_frontend(w http.ResponseWriter, r *http.Request) { 1002 | opt := struct { 1003 | update_time C.time_t 1004 | }{} 1005 | 1006 | obj := make(object_map) 1007 | obj.Add(&opt) 1008 | 1009 | obj.Run(w, r, func() { 1010 | var slres *C.front_end_info_msg_t 1011 | 1012 | ret := C.slurm_load_front_end(opt.update_time, &slres) 1013 | 1014 | if ret != 0 { 1015 | slurm_error(w, r) 1016 | return 1017 | } 1018 | 1019 | data := unsafe.Pointer(slres.front_end_array) 1020 | count := int(slres.record_count) 1021 | carray := *(*[]C.front_end_info_t)(unsafe.Pointer(&reflect.SliceHeader{ 1022 | Data: uintptr(data), 1023 | Len: count, 1024 | Cap: count, 1025 | })) 1026 | 1027 | res := get_res(slres) 1028 | array := make([]*table, count) 1029 | 1030 | for i := 0; i < count; i++ { 1031 | array[i] = get_res(&carray[i]) 1032 | } 1033 | 1034 | (*res)["FrontEndArray"] = array 1035 | 1036 | C.slurm_free_front_end_info_msg(slres) 1037 | 1038 | w.Header().Set("Content-Type", "application/json") 1039 | json.NewEncoder(w).Encode(&res) 1040 | }) 1041 | } 1042 | 1043 | func update_frontend(w http.ResponseWriter, r *http.Request) { 1044 | var slreq C.update_front_end_msg_t 1045 | C.slurm_init_update_front_end_msg(&slreq) 1046 | 1047 | obj := make(object_map) 1048 | obj.Add(&slreq) 1049 | 1050 | obj.Run(w, r, func() { 1051 | ret := C.slurm_update_front_end(&slreq) 1052 | 1053 | if ret != 0 { 1054 | slurm_error(w, r) 1055 | return 1056 | } 1057 | }) 1058 | } 1059 | 1060 | func lookup_job(w http.ResponseWriter, r *http.Request) { 1061 | opt := struct { 1062 | job_id C.uint32_t 1063 | }{} 1064 | 1065 | obj := make(object_map) 1066 | obj.Add(&opt) 1067 | 1068 | obj.Run(w, r, func() { 1069 | var slres *C.resource_allocation_response_msg_t 1070 | 1071 | ret := C.slurm_allocation_lookup(opt.job_id, &slres) 1072 | 1073 | if ret != 0 { 1074 | slurm_error(w, r) 1075 | return 1076 | } 1077 | 1078 | res := get_res(slres) 1079 | 1080 | C.slurm_free_resource_allocation_response_msg(slres) 1081 | 1082 | w.Header().Set("Content-Type", "application/json") 1083 | json.NewEncoder(w).Encode(&res) 1084 | }) 1085 | } 1086 | 1087 | func alloc_job(w http.ResponseWriter, r *http.Request) { 1088 | var slreq C.job_desc_msg_t 1089 | C.slurm_init_job_desc_msg(&slreq) 1090 | 1091 | obj := make(object_map) 1092 | obj.Add(&slreq) 1093 | 1094 | obj.Run(w, r, func() { 1095 | var slres *C.resource_allocation_response_msg_t 1096 | 1097 | ret := C.slurm_allocate_resources(&slreq, &slres) 1098 | 1099 | if ret != 0 { 1100 | slurm_error(w, r) 1101 | return 1102 | } 1103 | 1104 | res := get_res(slres) 1105 | 1106 | C.slurm_free_resource_allocation_response_msg(slres) 1107 | 1108 | w.Header().Set("Content-Type", "application/json") 1109 | json.NewEncoder(w).Encode(&res) 1110 | }) 1111 | } 1112 | 1113 | func load_ctl_conf(w http.ResponseWriter, r *http.Request) { 1114 | opt := struct { 1115 | update_time C.time_t 1116 | }{} 1117 | 1118 | obj := make(object_map) 1119 | obj.Add(&opt) 1120 | 1121 | obj.Run(w, r, func() { 1122 | var slres *C.slurm_conf_t 1123 | 1124 | ret := C.slurm_load_ctl_conf(opt.update_time, &slres) 1125 | 1126 | if ret != 0 { 1127 | slurm_error(w, r) 1128 | return 1129 | } 1130 | 1131 | res := get_res(slres) 1132 | 1133 | C.slurm_free_ctl_conf(slres) 1134 | 1135 | w.Header().Set("Content-Type", "application/json") 1136 | json.NewEncoder(w).Encode(&res) 1137 | }) 1138 | } 1139 | 1140 | /* 1141 | func tasks_checkpoint(w http.ResponseWriter, r *http.Request) { 1142 | opt := struct { 1143 | job_id C.uint32_t 1144 | step_id C.uint16_t // XXX 1145 | begin_time C.time_t 1146 | image_dir *C.char 1147 | max_wait C.uint16_t 1148 | node_list *C.char 1149 | }{} 1150 | 1151 | obj := make(object_map) 1152 | obj.Add(&obj) 1153 | 1154 | obj.Run(w,r, func(){ 1155 | ret := C.slurm_checkpoint_tasks( 1156 | opt.job_id, 1157 | opt.step_id, 1158 | opt.begin_time, 1159 | opt.image_dir, 1160 | opt.max_wait, 1161 | opt.node_list, 1162 | ) 1163 | 1164 | if ret != 0 { 1165 | slurm_error(w,r) 1166 | return 1167 | } 1168 | }) 1169 | } 1170 | */ 1171 | 1172 | func main() { 1173 | var ( 1174 | addr = flag.String("addr", ":8443", "adresse to serve") 1175 | cert = flag.String("cert", "server.crt", "certificate") 1176 | key = flag.String("key", "server.key", "certificate key") 1177 | ca = flag.String("ca", "ca.crt", "ca certificate") 1178 | ) 1179 | 1180 | flag.Parse() 1181 | 1182 | ca_cert, err := ioutil.ReadFile(*ca) 1183 | 1184 | if err != nil { 1185 | log.Fatal(err) 1186 | } 1187 | 1188 | ca_pool := x509.NewCertPool() 1189 | ca_pool.AppendCertsFromPEM(ca_cert) 1190 | 1191 | server := &http.Server{ 1192 | Addr: *addr, 1193 | TLSConfig: &tls.Config{ 1194 | ClientAuth: tls.RequireAndVerifyClientCert, 1195 | ClientCAs: ca_pool, 1196 | }, 1197 | } 1198 | 1199 | // this api is only for test... no comment :) 1200 | 1201 | http.HandleFunc("/nodes", load_node) 1202 | http.HandleFunc("/node/update", update_node) 1203 | 1204 | http.HandleFunc("/licenses", load_licenses) 1205 | 1206 | http.HandleFunc("/conf", load_ctl_conf) 1207 | 1208 | http.HandleFunc("/jobs", load_jobs) 1209 | http.HandleFunc("/job/alloc", alloc_job) 1210 | http.HandleFunc("/job/submit", submit_batch_job) 1211 | http.HandleFunc("/job/lookup", lookup_job) 1212 | http.HandleFunc("/job/update", update_job) 1213 | http.HandleFunc("/job/notify", notify_job) 1214 | http.HandleFunc("/job/kill", kill_job) 1215 | http.HandleFunc("/job/signal", signal_job) 1216 | http.HandleFunc("/job/complete", complete_job) 1217 | http.HandleFunc("/job/suspend", suspend_job) 1218 | http.HandleFunc("/job/resume", resume_job) 1219 | http.HandleFunc("/job/requeue", requeue_job) 1220 | 1221 | http.HandleFunc("/job/step/kill", kill_job_step) 1222 | http.HandleFunc("/job/step/signal", signal_job_step) 1223 | http.HandleFunc("/job/step/terminate", terminate_job_step) 1224 | 1225 | /* TODO 1226 | http.HandleFunc("/checkpoint/able", able_checkpoint) 1227 | http.HandleFunc("/checkpoint/enable", enable_checkpoint) 1228 | http.HandleFunc("/checkpoint/disable", disable_checkpoint) 1229 | http.HandleFunc("/checkpoint/create", create_checkpoint) 1230 | http.HandleFunc("/checkpoint/requeue", requeue_checkpoint) 1231 | http.HandleFunc("/checkpoint/vacate", vacate_checkpoint) 1232 | http.HandleFunc("/checkpoint/restart", restart_checkpoint) 1233 | http.HandleFunc("/checkpoint/complete", complete_checkpoint) 1234 | http.HandleFunc("/checkpoint/task/complete", task_complete_checkpoint) 1235 | http.HandleFunc("/checkpoint/error", error_checkpoint) 1236 | http.HandleFunc("/checkpoint/tasks", tasks_checkpoint) 1237 | */ 1238 | 1239 | http.HandleFunc("/frontends", load_frontend) 1240 | http.HandleFunc("/frontend/update", update_frontend) 1241 | 1242 | http.HandleFunc("/topologies", load_topo) 1243 | 1244 | http.HandleFunc("/partitions", load_partitions) 1245 | http.HandleFunc("/partition/create", create_partition) 1246 | http.HandleFunc("/partition/update", update_partition) 1247 | http.HandleFunc("/partition/delete", delete_partition) 1248 | 1249 | http.HandleFunc("/reservations", load_reservations) 1250 | http.HandleFunc("/reservation/create", create_reservation) 1251 | http.HandleFunc("/reservation/update", update_reservation) 1252 | http.HandleFunc("/reservation/delete", delete_reservation) 1253 | 1254 | http.HandleFunc("/triggers", get_triggers) 1255 | http.HandleFunc("/trigger/create", set_trigger) 1256 | http.HandleFunc("/trigger/delete", clear_trigger) 1257 | 1258 | http.HandleFunc("/ping", ping) 1259 | http.HandleFunc("/reconfigure", reconfigure) 1260 | http.HandleFunc("/shutdown", shutdown) 1261 | http.HandleFunc("/takeover", takeover) 1262 | 1263 | log.Println("Listening...") 1264 | 1265 | // disable goroutine ? 1266 | err = server.ListenAndServeTLS(*cert, *key) 1267 | 1268 | if err != nil { 1269 | log.Fatal(err) 1270 | } 1271 | } 1272 | --------------------------------------------------------------------------------