1/* 2 * Copyright (C) 2016 George W. Dunlap, Citrix Systems UK Ltd 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; 7 * version 2.1 of the License. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18// Package xenlight provides bindings to Xen's libxl C library. 19package xenlight 20 21/* 22 23#cgo LDFLAGS: -lxenlight -lyajl -lxentoollog 24#include <stdlib.h> 25#include <libxl.h> 26#include <libxl_utils.h> 27 28#define INVALID_DOMID_TYPED ((uint32_t) INVALID_DOMID) 29 30static const libxl_childproc_hooks childproc_hooks = { .chldowner = libxl_sigchld_owner_mainloop }; 31 32void xenlight_set_chldproc(libxl_ctx *ctx) { 33 libxl_childproc_setmode(ctx, &childproc_hooks, NULL); 34} 35*/ 36import "C" 37 38/* 39 * Other flags that may be needed at some point: 40 * -lnl-route-3 -lnl-3 41 * 42 * To get back to static linking: 43 * #cgo LDFLAGS: -lxenlight -lyajl_s -lxengnttab -lxenstore -lxenguest -lxentoollog -lxenevtchn -lxenctrl -lxenforeignmemory -lxencall -lz -luuid -lutil 44 */ 45 46import ( 47 "fmt" 48 "os" 49 "os/signal" 50 "syscall" 51 "unsafe" 52) 53 54var libxlErrors = map[Error]string{ 55 ErrorNonspecific: "Non-specific error", 56 ErrorVersion: "Wrong version", 57 ErrorFail: "Failed", 58 ErrorNi: "Not Implemented", 59 ErrorNomem: "No memory", 60 ErrorInval: "Invalid argument", 61 ErrorBadfail: "Bad Fail", 62 ErrorGuestTimedout: "Guest timed out", 63 ErrorTimedout: "Timed out", 64 ErrorNoparavirt: "No Paravirtualization", 65 ErrorNotReady: "Not ready", 66 ErrorOseventRegFail: "OS event registration failed", 67 ErrorBufferfull: "Buffer full", 68 ErrorUnknownChild: "Unknown child", 69 ErrorLockFail: "Lock failed", 70 ErrorJsonConfigEmpty: "JSON config empty", 71 ErrorDeviceExists: "Device exists", 72 ErrorCheckpointDevopsDoesNotMatch: "Checkpoint devops does not match", 73 ErrorCheckpointDeviceNotSupported: "Checkpoint device not supported", 74 ErrorVnumaConfigInvalid: "VNUMA config invalid", 75 ErrorDomainNotfound: "Domain not found", 76 ErrorAborted: "Aborted", 77 ErrorNotfound: "Not found", 78 ErrorDomainDestroyed: "Domain destroyed", 79 ErrorFeatureRemoved: "Feature removed", 80} 81 82const ( 83 DomidInvalid Domid = Domid(C.INVALID_DOMID_TYPED) 84) 85 86func (e Error) Error() string { 87 if s, ok := libxlErrors[e]; ok { 88 return s 89 } 90 91 return fmt.Sprintf("libxl error: %d", e) 92} 93 94// Context represents a libxl_ctx. 95type Context struct { 96 ctx *C.libxl_ctx 97 logger *C.xentoollog_logger_stdiostream 98 sigchld chan os.Signal 99 sigchldDone chan struct{} 100} 101 102// Golang always unmasks SIGCHLD, and internally has ways of 103// distributing SIGCHLD to multiple recipients. libxl has provision 104// for this model: just tell it when a SIGCHLD happened, and it will 105// look after its own processes. 106// 107// This should "play nicely" with other users of SIGCHLD as long as 108// they don't reap libxl's processes. 109// 110// Every context needs to be notified on each SIGCHLD; so spin up a 111// new goroutine for each context. If there are a large number of 112// contexts, this means each context will be woken up looking through 113// its own list of children. 114// 115// The alternate would be to register a fork callback, such that the 116// xenlight package can make a single list of all children, and only 117// notify the specific libxl context(s) that have children woken. But 118// it's not clear to me this will be much more work than having the 119// xenlight go library do the same thing; doing it in separate go 120// threads has the potential to do it in parallel. Leave that as an 121// optimization for later if it turns out to be a bottleneck. 122func sigchldHandler(ctx *Context) { 123 for _ = range ctx.sigchld { 124 C.libxl_childproc_sigchld_occurred(ctx.ctx) 125 } 126 close(ctx.sigchldDone) 127} 128 129// NewContext returns a new Context. 130func NewContext() (ctx *Context, err error) { 131 ctx = &Context{} 132 133 defer func() { 134 if err != nil { 135 ctx.Close() 136 ctx = nil 137 } 138 }() 139 140 // Create a logger 141 ctx.logger = C.xtl_createlogger_stdiostream(C.stderr, C.XTL_ERROR, 0) 142 143 // Allocate a context 144 ret := C.libxl_ctx_alloc(&ctx.ctx, C.LIBXL_VERSION, 0, 145 (*C.xentoollog_logger)(unsafe.Pointer(ctx.logger))) 146 if ret != 0 { 147 return ctx, Error(ret) 148 } 149 150 // Tell libxl that we'll be dealing with SIGCHLD... 151 C.xenlight_set_chldproc(ctx.ctx) 152 153 // ...and arrange to keep that promise. 154 ctx.sigchld = make(chan os.Signal, 2) 155 ctx.sigchldDone = make(chan struct{}, 1) 156 signal.Notify(ctx.sigchld, syscall.SIGCHLD) 157 158 // This goroutine will run until the ctx.sigchld is closed in 159 // ctx.Close(); at which point it will close ctx.sigchldDone. 160 go sigchldHandler(ctx) 161 162 return ctx, nil 163} 164 165// Close closes the Context. 166func (ctx *Context) Close() error { 167 // Tell our SIGCHLD notifier to shut down, and wait for it to exit 168 // before we free the context. 169 if ctx.sigchld != nil { 170 signal.Stop(ctx.sigchld) 171 close(ctx.sigchld) 172 173 <-ctx.sigchldDone 174 175 ctx.sigchld = nil 176 ctx.sigchldDone = nil 177 } 178 179 if ctx.ctx != nil { 180 ret := C.libxl_ctx_free(ctx.ctx) 181 if ret != 0 { 182 return Error(ret) 183 } 184 ctx.ctx = nil 185 } 186 187 if ctx.logger != nil { 188 C.xtl_logger_destroy((*C.xentoollog_logger)(unsafe.Pointer(ctx.logger))) 189 ctx.logger = nil 190 } 191 192 return nil 193} 194 195/* 196 * Types: Builtins 197 */ 198 199type Domid uint32 200 201// NameToDomid returns the Domid for a domain, given its name, if it exists. 202// 203// NameToDomid does not guarantee that the domid associated with name at 204// the time NameToDomid is called is the same as the domid associated with 205// name at the time NameToDomid returns. 206func (Ctx *Context) NameToDomid(name string) (Domid, error) { 207 var domid C.uint32_t 208 209 cname := C.CString(name) 210 defer C.free(unsafe.Pointer(cname)) 211 212 if ret := C.libxl_name_to_domid(Ctx.ctx, cname, &domid); ret != 0 { 213 return DomidInvalid, Error(ret) 214 } 215 216 return Domid(domid), nil 217} 218 219// DomidToName returns the name for a domain, given its domid. If there 220// is no domain with the given domid, DomidToName will return the empty 221// string. 222// 223// DomidToName does not guarantee that the name (if any) associated with domid 224// at the time DomidToName is called is the same as the name (if any) associated 225// with domid at the time DomidToName returns. 226func (Ctx *Context) DomidToName(domid Domid) string { 227 cname := C.libxl_domid_to_name(Ctx.ctx, C.uint32_t(domid)) 228 defer C.free(unsafe.Pointer(cname)) 229 230 return C.GoString(cname) 231} 232 233// Devid is a device ID. 234type Devid int 235 236// Uuid is a domain UUID. 237type Uuid [16]byte 238 239// String formats a Uuid in the form "xxxx-xx-xx-xx-xxxxxx". 240func (u Uuid) String() string { 241 s := "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x" 242 opts := make([]interface{}, 16) 243 244 for i, v := range u { 245 opts[i] = v 246 } 247 248 return fmt.Sprintf(s, opts...) 249} 250 251func (u *Uuid) fromC(c *C.libxl_uuid) error { 252 for i := range *u { 253 u[i] = byte(c.uuid[i]) 254 } 255 256 return nil 257} 258 259func (u *Uuid) toC(cu *C.libxl_uuid) error { 260 for i, v := range u { 261 cu.uuid[i] = C.uint8_t(v) 262 } 263 264 return nil 265} 266 267// defboolVal represents a defbool value. 268type defboolVal int 269 270const ( 271 defboolDefault defboolVal = 0 272 defboolFalse defboolVal = -1 273 defboolTrue defboolVal = 1 274) 275 276// Defbool represents a libxl_defbool. 277type Defbool struct { 278 val defboolVal 279} 280 281func (d Defbool) String() string { 282 switch d.val { 283 case defboolDefault: 284 return "<default>" 285 case defboolFalse: 286 return "False" 287 case defboolTrue: 288 return "True" 289 } 290 291 return "" 292} 293 294// Set sets the value of the Defbool. 295func (d *Defbool) Set(b bool) { 296 if b { 297 d.val = defboolTrue 298 return 299 } 300 d.val = defboolFalse 301} 302 303// Unset resets the Defbool to default value. 304func (d *Defbool) Unset() { 305 d.val = defboolDefault 306} 307 308// SetIfDefault sets the value of Defbool only if 309// its current value is default. 310func (d *Defbool) SetIfDefault(b bool) { 311 if d.IsDefault() { 312 d.Set(b) 313 } 314} 315 316// IsDefault returns true if the value of Defbool 317// is default, returns false otherwise. 318func (d *Defbool) IsDefault() bool { 319 return d.val == defboolDefault 320} 321 322// Val returns the boolean value associated with the 323// Defbool value. An error is returned if the value 324// is default. 325func (d *Defbool) Val() (bool, error) { 326 if d.IsDefault() { 327 return false, fmt.Errorf("%v: cannot take value of default defbool", ErrorInval) 328 } 329 330 return (d.val > 0), nil 331} 332 333func (d *Defbool) fromC(c *C.libxl_defbool) error { 334 if C.libxl_defbool_is_default(*c) { 335 d.val = defboolDefault 336 return nil 337 } 338 339 if C.libxl_defbool_val(*c) { 340 d.val = defboolTrue 341 return nil 342 } 343 344 d.val = defboolFalse 345 346 return nil 347} 348 349func (d *Defbool) toC(cd *C.libxl_defbool) error { 350 if !d.IsDefault() { 351 val, _ := d.Val() 352 C.libxl_defbool_set(cd, C.bool(val)) 353 } 354 355 return nil 356} 357 358// Mac represents a libxl_mac, or simply a MAC address. 359type Mac [6]byte 360 361// String formats a Mac address to string representation. 362func (mac Mac) String() string { 363 s := "%02x:%02x:%02x:%02x:%02x:%02x" 364 opts := make([]interface{}, 6) 365 366 for i, v := range mac { 367 opts[i] = v 368 } 369 370 return fmt.Sprintf(s, opts...) 371} 372 373func (mac *Mac) fromC(cmac *C.libxl_mac) error { 374 for i := range *mac { 375 mac[i] = byte(cmac[i]) 376 } 377 378 return nil 379} 380 381func (mac Mac) toC(cm *C.libxl_mac) error { 382 for i, v := range mac { 383 (*cm)[i] = C.uint8_t(v) 384 } 385 386 return nil 387} 388 389// MsVmGenid represents a libxl_ms_vm_genid. 390type MsVmGenid [int(C.LIBXL_MS_VM_GENID_LEN)]byte 391 392func (mvg *MsVmGenid) fromC(cmvg *C.libxl_ms_vm_genid) error { 393 for i := range *mvg { 394 mvg[i] = byte(cmvg.bytes[i]) 395 } 396 397 return nil 398} 399 400func (mvg *MsVmGenid) toC(cmvg *C.libxl_ms_vm_genid) error { 401 for i, v := range mvg { 402 cmvg.bytes[i] = C.uint8_t(v) 403 } 404 405 return nil 406} 407 408// EvLink represents a libxl_ev_link. 409// 410// Represented as an empty struct for now, as there is no 411// apparent need for the internals of this type to be exposed 412// through the Go package. 413type EvLink struct{} 414 415func (el *EvLink) fromC(cel *C.libxl_ev_link) error { return nil } 416func (el *EvLink) toC(cel *C.libxl_ev_link) (err error) { return } 417 418// CpuidPolicyList represents a libxl_cpuid_policy_list. 419// 420// The value of CpuidPolicyList is honored when used as input to libxl. If 421// a struct contains a field of type CpuidPolicyList, that field will be left 422// empty when it is returned from libxl. 423type CpuidPolicyList string 424 425func (cpl *CpuidPolicyList) fromC(ccpl *C.libxl_cpuid_policy_list) error { *cpl = ""; return nil } 426 427func (cpl CpuidPolicyList) toC(ccpl *C.libxl_cpuid_policy_list) error { 428 if cpl == "" { 429 *ccpl = nil 430 return nil 431 } 432 433 s := C.CString(string(cpl)) 434 defer C.free(unsafe.Pointer(s)) 435 436 ret := C.libxl_cpuid_parse_config(ccpl, s) 437 if ret != 0 { 438 C.libxl_cpuid_dispose(ccpl) 439 440 // libxl_cpuid_parse_config doesn't return a normal libxl error. 441 return ErrorInval 442 } 443 444 return nil 445} 446 447// Hwcap represents a libxl_hwcap. 448type Hwcap [8]uint32 449 450func (hwcap *Hwcap) fromC(chwcap *C.libxl_hwcap) error { 451 for i := range *hwcap { 452 hwcap[i] = uint32(chwcap[i]) 453 } 454 455 return nil 456} 457 458func (hwcap *Hwcap) toC(chwcap *C.libxl_hwcap) error { 459 for i, v := range hwcap { 460 (*chwcap)[i] = C.uint32_t(v) 461 } 462 463 return nil 464} 465 466// KeyValueList represents a libxl_key_value_list. 467// 468// Represented as an empty struct for now, as there is no 469// apparent need for this type to be exposed through the 470// Go package. 471type KeyValueList struct{} 472 473func (kvl KeyValueList) fromC(ckvl *C.libxl_key_value_list) error { return nil } 474func (kvl KeyValueList) toC(ckvl *C.libxl_key_value_list) (err error) { return } 475 476// StringList represents a libxl_string_list. 477type StringList []string 478 479func (sl *StringList) fromC(csl *C.libxl_string_list) error { 480 size := int(C.libxl_string_list_length(csl)) 481 list := (*[1 << 30]*C.char)(unsafe.Pointer(csl))[:size:size] 482 483 *sl = make([]string, size) 484 485 for i, v := range list { 486 (*sl)[i] = C.GoString(v) 487 } 488 489 return nil 490} 491 492func (sl StringList) toC(csl *C.libxl_string_list) error { 493 var char *C.char 494 size := len(sl) 495 *csl = (C.libxl_string_list)(C.malloc(C.ulong(size) * C.ulong(unsafe.Sizeof(char)))) 496 clist := (*[1 << 30]*C.char)(unsafe.Pointer(csl))[:size:size] 497 498 for i, v := range sl { 499 clist[i] = C.CString(v) 500 } 501 502 return nil 503} 504 505// Bitmap represents a libxl_bitmap. 506// 507// Implement the Go bitmap type such that the underlying data can 508// easily be copied in and out. NB that we still have to do copies 509// both directions, because cgo runtime restrictions forbid passing to 510// a C function a pointer to a Go-allocated structure which contains a 511// pointer. 512type Bitmap struct { 513 // typedef struct { 514 // uint32_t size; /* number of bytes in map */ 515 // uint8_t *map; 516 // } libxl_bitmap; 517 bitmap []C.uint8_t 518} 519 520func (bm *Bitmap) fromC(cbm *C.libxl_bitmap) error { 521 bm.bitmap = nil 522 if size := int(cbm.size); size > 0 { 523 // Alloc a Go slice for the bytes 524 bm.bitmap = make([]C.uint8_t, size) 525 526 // Make a slice pointing to the C array 527 cs := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] 528 529 // And copy the C array into the Go array 530 copy(bm.bitmap, cs) 531 } 532 533 return nil 534} 535 536func (bm *Bitmap) toC(cbm *C.libxl_bitmap) error { 537 size := len(bm.bitmap) 538 cbm.size = C.uint32_t(size) 539 if cbm.size > 0 { 540 cbm._map = (*C.uint8_t)(C.malloc(C.ulong(cbm.size) * C.sizeof_uint8_t)) 541 cs := (*[1 << 31]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] 542 543 copy(cs, bm.bitmap) 544 } 545 546 return nil 547} 548 549func (sr ShutdownReason) String() (str string) { 550 cstr := C.libxl_shutdown_reason_to_string(C.libxl_shutdown_reason(sr)) 551 str = C.GoString(cstr) 552 553 return 554} 555 556func (dt DomainType) String() (str string) { 557 cstr := C.libxl_domain_type_to_string(C.libxl_domain_type(dt)) 558 str = C.GoString(cstr) 559 560 return 561} 562 563// const char *libxl_scheduler_to_string(libxl_scheduler p); 564func (s Scheduler) String() string { 565 cs := C.libxl_scheduler_to_string(C.libxl_scheduler(s)) 566 // No need to free const return value 567 568 return C.GoString(cs) 569} 570 571// int libxl_scheduler_from_string(const char *s, libxl_scheduler *e); 572func (s *Scheduler) FromString(gstr string) (err error) { 573 *s, err = SchedulerFromString(gstr) 574 return 575} 576 577func SchedulerFromString(name string) (s Scheduler, err error) { 578 cname := C.CString(name) 579 defer C.free(unsafe.Pointer(cname)) 580 581 var cs C.libxl_scheduler 582 583 ret := C.libxl_scheduler_from_string(cname, &cs) 584 if ret != 0 { 585 err = Error(-ret) 586 return 587 } 588 589 s = Scheduler(cs) 590 591 return 592} 593 594// libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out); 595// void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool); 596func (Ctx *Context) ListCpupool() (list []Cpupoolinfo) { 597 var nbPool C.int 598 599 c_cpupool_list := C.libxl_list_cpupool(Ctx.ctx, &nbPool) 600 601 defer C.libxl_cpupoolinfo_list_free(c_cpupool_list, nbPool) 602 603 if int(nbPool) == 0 { 604 return 605 } 606 607 // Magic 608 cpupoolListSlice := (*[1 << 30]C.libxl_cpupoolinfo)(unsafe.Pointer(c_cpupool_list))[:nbPool:nbPool] 609 for i := range cpupoolListSlice { 610 var info Cpupoolinfo 611 _ = info.fromC(&cpupoolListSlice[i]) 612 list = append(list, info) 613 } 614 615 return 616} 617 618// int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid); 619func (Ctx *Context) CpupoolInfo(Poolid uint32) (pool Cpupoolinfo, err error) { 620 var c_cpupool C.libxl_cpupoolinfo 621 622 ret := C.libxl_cpupool_info(Ctx.ctx, &c_cpupool, C.uint32_t(Poolid)) 623 if ret != 0 { 624 err = Error(-ret) 625 return 626 } 627 defer C.libxl_cpupoolinfo_dispose(&c_cpupool) 628 629 err = pool.fromC(&c_cpupool) 630 631 return 632} 633 634// int libxl_cpupool_create(libxl_ctx *ctx, const char *name, 635// libxl_scheduler sched, 636// libxl_bitmap cpumap, libxl_uuid *uuid, 637// uint32_t *poolid); 638// FIXME: uuid 639// FIXME: Setting poolid 640func (Ctx *Context) CpupoolCreate(Name string, Scheduler Scheduler, Cpumap Bitmap) (err error, Poolid uint32) { 641 poolid := C.uint32_t(C.LIBXL_CPUPOOL_POOLID_ANY) 642 name := C.CString(Name) 643 defer C.free(unsafe.Pointer(name)) 644 645 // For now, just do what xl does, and make a new uuid every time we create the pool 646 var uuid C.libxl_uuid 647 C.libxl_uuid_generate(&uuid) 648 649 var cbm C.libxl_bitmap 650 if err = Cpumap.toC(&cbm); err != nil { 651 return 652 } 653 defer C.libxl_bitmap_dispose(&cbm) 654 655 ret := C.libxl_cpupool_create(Ctx.ctx, name, C.libxl_scheduler(Scheduler), 656 cbm, &uuid, &poolid) 657 if ret != 0 { 658 err = Error(-ret) 659 return 660 } 661 662 Poolid = uint32(poolid) 663 664 return 665} 666 667// int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid); 668func (Ctx *Context) CpupoolDestroy(Poolid uint32) (err error) { 669 ret := C.libxl_cpupool_destroy(Ctx.ctx, C.uint32_t(Poolid)) 670 if ret != 0 { 671 err = Error(-ret) 672 return 673 } 674 675 return 676} 677 678// int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu); 679func (Ctx *Context) CpupoolCpuadd(Poolid uint32, Cpu int) (err error) { 680 ret := C.libxl_cpupool_cpuadd(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) 681 if ret != 0 { 682 err = Error(-ret) 683 return 684 } 685 686 return 687} 688 689// int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid, 690// const libxl_bitmap *cpumap); 691func (Ctx *Context) CpupoolCpuaddCpumap(Poolid uint32, Cpumap Bitmap) (err error) { 692 var cbm C.libxl_bitmap 693 if err = Cpumap.toC(&cbm); err != nil { 694 return 695 } 696 defer C.libxl_bitmap_dispose(&cbm) 697 698 ret := C.libxl_cpupool_cpuadd_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm) 699 if ret != 0 { 700 err = Error(-ret) 701 return 702 } 703 704 return 705} 706 707// int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu); 708func (Ctx *Context) CpupoolCpuremove(Poolid uint32, Cpu int) (err error) { 709 ret := C.libxl_cpupool_cpuremove(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) 710 if ret != 0 { 711 err = Error(-ret) 712 return 713 } 714 715 return 716} 717 718// int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid, 719// const libxl_bitmap *cpumap); 720func (Ctx *Context) CpupoolCpuremoveCpumap(Poolid uint32, Cpumap Bitmap) (err error) { 721 var cbm C.libxl_bitmap 722 if err = Cpumap.toC(&cbm); err != nil { 723 return 724 } 725 defer C.libxl_bitmap_dispose(&cbm) 726 727 ret := C.libxl_cpupool_cpuremove_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm) 728 if ret != 0 { 729 err = Error(-ret) 730 return 731 } 732 733 return 734} 735 736// int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid); 737func (Ctx *Context) CpupoolRename(Name string, Poolid uint32) (err error) { 738 name := C.CString(Name) 739 defer C.free(unsafe.Pointer(name)) 740 741 ret := C.libxl_cpupool_rename(Ctx.ctx, name, C.uint32_t(Poolid)) 742 if ret != 0 { 743 err = Error(-ret) 744 return 745 } 746 747 return 748} 749 750// int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); 751func (Ctx *Context) CpupoolCpuaddNode(Poolid uint32, Node int) (Cpus int, err error) { 752 ccpus := C.int(0) 753 754 ret := C.libxl_cpupool_cpuadd_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) 755 if ret != 0 { 756 err = Error(-ret) 757 return 758 } 759 760 Cpus = int(ccpus) 761 762 return 763} 764 765// int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); 766func (Ctx *Context) CpupoolCpuremoveNode(Poolid uint32, Node int) (Cpus int, err error) { 767 ccpus := C.int(0) 768 769 ret := C.libxl_cpupool_cpuremove_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) 770 if ret != 0 { 771 err = Error(-ret) 772 return 773 } 774 775 Cpus = int(ccpus) 776 777 return 778} 779 780// int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid); 781func (Ctx *Context) CpupoolMovedomain(Poolid uint32, Id Domid) (err error) { 782 ret := C.libxl_cpupool_movedomain(Ctx.ctx, C.uint32_t(Poolid), C.uint32_t(Id)) 783 if ret != 0 { 784 err = Error(-ret) 785 return 786 } 787 788 return 789} 790 791// 792// Utility functions 793// 794func (Ctx *Context) CpupoolFindByName(name string) (info Cpupoolinfo, found bool) { 795 plist := Ctx.ListCpupool() 796 797 for i := range plist { 798 if plist[i].PoolName == name { 799 found = true 800 info = plist[i] 801 return 802 } 803 } 804 return 805} 806 807func (Ctx *Context) CpupoolMakeFree(Cpumap Bitmap) (err error) { 808 plist := Ctx.ListCpupool() 809 810 for i := range plist { 811 var Intersection Bitmap 812 Intersection = Cpumap.And(plist[i].Cpumap) 813 if !Intersection.IsEmpty() { 814 err = Ctx.CpupoolCpuremoveCpumap(plist[i].Poolid, Intersection) 815 if err != nil { 816 return 817 } 818 } 819 } 820 return 821} 822 823/* 824 * Bitmap operations 825 */ 826 827func (bm *Bitmap) Test(bit int) bool { 828 ubit := uint(bit) 829 if bit > bm.Max() || bm.bitmap == nil { 830 return false 831 } 832 833 return (bm.bitmap[bit/8] & (1 << (ubit & 7))) != 0 834} 835 836func (bm *Bitmap) Set(bit int) { 837 ibit := bit / 8 838 if ibit+1 > len(bm.bitmap) { 839 bm.bitmap = append(bm.bitmap, make([]C.uint8_t, ibit+1-len(bm.bitmap))...) 840 } 841 842 bm.bitmap[ibit] |= 1 << (uint(bit) & 7) 843} 844 845func (bm *Bitmap) SetRange(start int, end int) { 846 for i := start; i <= end; i++ { 847 bm.Set(i) 848 } 849} 850 851func (bm *Bitmap) Clear(bit int) { 852 ubit := uint(bit) 853 if bit > bm.Max() || bm.bitmap == nil { 854 return 855 } 856 857 bm.bitmap[bit/8] &= ^(1 << (ubit & 7)) 858} 859 860func (bm *Bitmap) ClearRange(start int, end int) { 861 for i := start; i <= end; i++ { 862 bm.Clear(i) 863 } 864} 865 866func (bm *Bitmap) Max() int { 867 return len(bm.bitmap)*8 - 1 868} 869 870func (bm *Bitmap) IsEmpty() bool { 871 for i := 0; i < len(bm.bitmap); i++ { 872 if bm.bitmap[i] != 0 { 873 return false 874 } 875 } 876 return true 877} 878 879func (a Bitmap) And(b Bitmap) (c Bitmap) { 880 var max, min int 881 if len(a.bitmap) > len(b.bitmap) { 882 max = len(a.bitmap) 883 min = len(b.bitmap) 884 } else { 885 max = len(b.bitmap) 886 min = len(a.bitmap) 887 } 888 c.bitmap = make([]C.uint8_t, max) 889 890 for i := 0; i < min; i++ { 891 c.bitmap[i] = a.bitmap[i] & b.bitmap[i] 892 } 893 return 894} 895 896func (bm Bitmap) String() (s string) { 897 lastOnline := false 898 crange := false 899 printed := false 900 var i int 901 /// --x-xxxxx-x -> 2,4-8,10 902 /// --x-xxxxxxx -> 2,4-10 903 for i = 0; i <= bm.Max(); i++ { 904 if bm.Test(i) { 905 if !lastOnline { 906 // Switching offline -> online, print this cpu 907 if printed { 908 s += "," 909 } 910 s += fmt.Sprintf("%d", i) 911 printed = true 912 } else if !crange { 913 // last was online, but we're not in a range; print - 914 crange = true 915 s += "-" 916 } else { 917 // last was online, we're in a range, nothing else to do 918 } 919 lastOnline = true 920 } else { 921 if lastOnline { 922 // Switching online->offline; do we need to end a range? 923 if crange { 924 s += fmt.Sprintf("%d", i-1) 925 } 926 } 927 lastOnline = false 928 crange = false 929 } 930 } 931 if lastOnline { 932 // Switching online->offline; do we need to end a range? 933 if crange { 934 s += fmt.Sprintf("%d", i-1) 935 } 936 } 937 938 return 939} 940 941//int libxl_get_max_cpus(libxl_ctx *ctx); 942func (Ctx *Context) GetMaxCpus() (maxCpus int, err error) { 943 ret := C.libxl_get_max_cpus(Ctx.ctx) 944 if ret < 0 { 945 err = Error(-ret) 946 return 947 } 948 maxCpus = int(ret) 949 return 950} 951 952//int libxl_get_online_cpus(libxl_ctx *ctx); 953func (Ctx *Context) GetOnlineCpus() (onCpus int, err error) { 954 ret := C.libxl_get_online_cpus(Ctx.ctx) 955 if ret < 0 { 956 err = Error(-ret) 957 return 958 } 959 onCpus = int(ret) 960 return 961} 962 963//int libxl_get_max_nodes(libxl_ctx *ctx); 964func (Ctx *Context) GetMaxNodes() (maxNodes int, err error) { 965 ret := C.libxl_get_max_nodes(Ctx.ctx) 966 if ret < 0 { 967 err = Error(-ret) 968 return 969 } 970 maxNodes = int(ret) 971 return 972} 973 974//int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb); 975func (Ctx *Context) GetFreeMemory() (memkb uint64, err error) { 976 var cmem C.uint64_t 977 ret := C.libxl_get_free_memory(Ctx.ctx, &cmem) 978 979 if ret < 0 { 980 err = Error(-ret) 981 return 982 } 983 984 memkb = uint64(cmem) 985 return 986 987} 988 989//int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo) 990func (Ctx *Context) GetPhysinfo() (physinfo *Physinfo, err error) { 991 var cphys C.libxl_physinfo 992 C.libxl_physinfo_init(&cphys) 993 defer C.libxl_physinfo_dispose(&cphys) 994 995 ret := C.libxl_get_physinfo(Ctx.ctx, &cphys) 996 997 if ret < 0 { 998 err = Error(ret) 999 return 1000 } 1001 err = physinfo.fromC(&cphys) 1002 1003 return 1004} 1005 1006//const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx); 1007func (Ctx *Context) GetVersionInfo() (info *VersionInfo, err error) { 1008 var cinfo *C.libxl_version_info 1009 1010 cinfo = C.libxl_get_version_info(Ctx.ctx) 1011 1012 err = info.fromC(cinfo) 1013 1014 return 1015} 1016 1017func (Ctx *Context) DomainInfo(Id Domid) (di *Dominfo, err error) { 1018 var cdi C.libxl_dominfo 1019 C.libxl_dominfo_init(&cdi) 1020 defer C.libxl_dominfo_dispose(&cdi) 1021 1022 ret := C.libxl_domain_info(Ctx.ctx, &cdi, C.uint32_t(Id)) 1023 1024 if ret != 0 { 1025 err = Error(-ret) 1026 return 1027 } 1028 1029 err = di.fromC(&cdi) 1030 1031 return 1032} 1033 1034func (Ctx *Context) DomainUnpause(Id Domid) (err error) { 1035 ret := C.libxl_domain_unpause(Ctx.ctx, C.uint32_t(Id), nil) 1036 1037 if ret != 0 { 1038 err = Error(-ret) 1039 } 1040 return 1041} 1042 1043//int libxl_domain_pause(libxl_ctx *ctx, uint32_t domain); 1044func (Ctx *Context) DomainPause(id Domid) (err error) { 1045 ret := C.libxl_domain_pause(Ctx.ctx, C.uint32_t(id), nil) 1046 1047 if ret != 0 { 1048 err = Error(-ret) 1049 } 1050 return 1051} 1052 1053//int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid); 1054func (Ctx *Context) DomainShutdown(id Domid) (err error) { 1055 ret := C.libxl_domain_shutdown(Ctx.ctx, C.uint32_t(id), nil) 1056 1057 if ret != 0 { 1058 err = Error(-ret) 1059 } 1060 return 1061} 1062 1063//int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid); 1064func (Ctx *Context) DomainReboot(id Domid) (err error) { 1065 ret := C.libxl_domain_reboot(Ctx.ctx, C.uint32_t(id), nil) 1066 1067 if ret != 0 { 1068 err = Error(-ret) 1069 } 1070 return 1071} 1072 1073//libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out); 1074//void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain); 1075func (Ctx *Context) ListDomain() (glist []Dominfo) { 1076 var nbDomain C.int 1077 clist := C.libxl_list_domain(Ctx.ctx, &nbDomain) 1078 defer C.libxl_dominfo_list_free(clist, nbDomain) 1079 1080 if int(nbDomain) == 0 { 1081 return 1082 } 1083 1084 gslice := (*[1 << 30]C.libxl_dominfo)(unsafe.Pointer(clist))[:nbDomain:nbDomain] 1085 for i := range gslice { 1086 var info Dominfo 1087 _ = info.fromC(&gslice[i]) 1088 glist = append(glist, info) 1089 } 1090 1091 return 1092} 1093 1094//libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, 1095// int *nb_vcpu, int *nr_cpus_out); 1096//void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus); 1097func (Ctx *Context) ListVcpu(id Domid) (glist []Vcpuinfo) { 1098 var nbVcpu C.int 1099 var nrCpu C.int 1100 1101 clist := C.libxl_list_vcpu(Ctx.ctx, C.uint32_t(id), &nbVcpu, &nrCpu) 1102 defer C.libxl_vcpuinfo_list_free(clist, nbVcpu) 1103 1104 if int(nbVcpu) == 0 { 1105 return 1106 } 1107 1108 gslice := (*[1 << 30]C.libxl_vcpuinfo)(unsafe.Pointer(clist))[:nbVcpu:nbVcpu] 1109 for i := range gslice { 1110 var info Vcpuinfo 1111 _ = info.fromC(&gslice[i]) 1112 glist = append(glist, info) 1113 } 1114 1115 return 1116} 1117 1118func (ct ConsoleType) String() (str string) { 1119 cstr := C.libxl_console_type_to_string(C.libxl_console_type(ct)) 1120 str = C.GoString(cstr) 1121 1122 return 1123} 1124 1125//int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num, 1126//libxl_console_type type, char **path); 1127func (Ctx *Context) ConsoleGetTty(id Domid, consNum int, conType ConsoleType) (path string, err error) { 1128 var cpath *C.char 1129 ret := C.libxl_console_get_tty(Ctx.ctx, C.uint32_t(id), C.int(consNum), C.libxl_console_type(conType), &cpath) 1130 if ret != 0 { 1131 err = Error(-ret) 1132 return 1133 } 1134 defer C.free(unsafe.Pointer(cpath)) 1135 1136 path = C.GoString(cpath) 1137 return 1138} 1139 1140//int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, 1141// char **path); 1142func (Ctx *Context) PrimaryConsoleGetTty(domid uint32) (path string, err error) { 1143 var cpath *C.char 1144 ret := C.libxl_primary_console_get_tty(Ctx.ctx, C.uint32_t(domid), &cpath) 1145 if ret != 0 { 1146 err = Error(-ret) 1147 return 1148 } 1149 defer C.free(unsafe.Pointer(cpath)) 1150 1151 path = C.GoString(cpath) 1152 return 1153} 1154 1155// DeviceNicAdd adds a nic to a domain. 1156func (Ctx *Context) DeviceNicAdd(domid Domid, nic *DeviceNic) error { 1157 var cnic C.libxl_device_nic 1158 1159 if err := nic.toC(&cnic); err != nil { 1160 return err 1161 } 1162 defer C.libxl_device_nic_dispose(&cnic) 1163 1164 ret := C.libxl_device_nic_add(Ctx.ctx, C.uint32_t(domid), &cnic, nil) 1165 if ret != 0 { 1166 return Error(ret) 1167 } 1168 1169 return nil 1170} 1171 1172// DeviceNicRemove removes a nic from a domain. 1173func (Ctx *Context) DeviceNicRemove(domid Domid, nic *DeviceNic) error { 1174 var cnic C.libxl_device_nic 1175 1176 if err := nic.toC(&cnic); err != nil { 1177 return err 1178 } 1179 defer C.libxl_device_nic_dispose(&cnic) 1180 1181 ret := C.libxl_device_nic_remove(Ctx.ctx, C.uint32_t(domid), &cnic, nil) 1182 if ret != 0 { 1183 return Error(ret) 1184 } 1185 1186 return nil 1187} 1188 1189// DevicePciAdd is used to passthrough a PCI device to a domain. 1190func (Ctx *Context) DevicePciAdd(domid Domid, pci *DevicePci) error { 1191 var cpci C.libxl_device_pci 1192 1193 if err := pci.toC(&cpci); err != nil { 1194 return err 1195 } 1196 defer C.libxl_device_pci_dispose(&cpci) 1197 1198 ret := C.libxl_device_pci_add(Ctx.ctx, C.uint32_t(domid), &cpci, nil) 1199 if ret != 0 { 1200 return Error(ret) 1201 } 1202 1203 return nil 1204} 1205 1206// DevicePciRemove removes a PCI device from a domain. 1207func (Ctx *Context) DevicePciRemove(domid Domid, pci *DevicePci) error { 1208 var cpci C.libxl_device_pci 1209 1210 if err := pci.toC(&cpci); err != nil { 1211 return err 1212 } 1213 defer C.libxl_device_pci_dispose(&cpci) 1214 1215 ret := C.libxl_device_pci_remove(Ctx.ctx, C.uint32_t(domid), &cpci, nil) 1216 if ret != 0 { 1217 return Error(ret) 1218 } 1219 1220 return nil 1221} 1222 1223// DeviceUsbdevAdd adds a USB device to a domain. 1224func (Ctx *Context) DeviceUsbdevAdd(domid Domid, usbdev *DeviceUsbdev) error { 1225 var cusbdev C.libxl_device_usbdev 1226 1227 if err := usbdev.toC(&cusbdev); err != nil { 1228 return err 1229 } 1230 defer C.libxl_device_usbdev_dispose(&cusbdev) 1231 1232 ret := C.libxl_device_usbdev_add(Ctx.ctx, C.uint32_t(domid), &cusbdev, nil) 1233 if ret != 0 { 1234 return Error(ret) 1235 } 1236 1237 return nil 1238} 1239 1240// DeviceUsbdevRemove removes a USB device from a domain. 1241func (Ctx *Context) DeviceUsbdevRemove(domid Domid, usbdev *DeviceUsbdev) error { 1242 var cusbdev C.libxl_device_usbdev 1243 1244 if err := usbdev.toC(&cusbdev); err != nil { 1245 return err 1246 } 1247 defer C.libxl_device_usbdev_dispose(&cusbdev) 1248 1249 ret := C.libxl_device_usbdev_remove(Ctx.ctx, C.uint32_t(domid), &cusbdev, nil) 1250 if ret != 0 { 1251 return Error(ret) 1252 } 1253 1254 return nil 1255} 1256 1257// DomainCreateNew creates a new domain. 1258func (Ctx *Context) DomainCreateNew(config *DomainConfig) (Domid, error) { 1259 var cdomid C.uint32_t 1260 var cconfig C.libxl_domain_config 1261 err := config.toC(&cconfig) 1262 if err != nil { 1263 return Domid(0), fmt.Errorf("converting domain config to C: %v", err) 1264 } 1265 defer C.libxl_domain_config_dispose(&cconfig) 1266 1267 ret := C.libxl_domain_create_new(Ctx.ctx, &cconfig, &cdomid, nil, nil) 1268 if ret != 0 { 1269 return Domid(0), Error(ret) 1270 } 1271 1272 return Domid(cdomid), nil 1273} 1274