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) + 1 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 clist[len(clist)-1] = nil 502 503 return nil 504} 505 506// Bitmap represents a libxl_bitmap. 507// 508// Implement the Go bitmap type such that the underlying data can 509// easily be copied in and out. NB that we still have to do copies 510// both directions, because cgo runtime restrictions forbid passing to 511// a C function a pointer to a Go-allocated structure which contains a 512// pointer. 513type Bitmap struct { 514 // typedef struct { 515 // uint32_t size; /* number of bytes in map */ 516 // uint8_t *map; 517 // } libxl_bitmap; 518 bitmap []C.uint8_t 519} 520 521func (bm *Bitmap) fromC(cbm *C.libxl_bitmap) error { 522 bm.bitmap = nil 523 if size := int(cbm.size); size > 0 { 524 // Alloc a Go slice for the bytes 525 bm.bitmap = make([]C.uint8_t, size) 526 527 // Make a slice pointing to the C array 528 cs := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] 529 530 // And copy the C array into the Go array 531 copy(bm.bitmap, cs) 532 } 533 534 return nil 535} 536 537func (bm *Bitmap) toC(cbm *C.libxl_bitmap) error { 538 size := len(bm.bitmap) 539 cbm.size = C.uint32_t(size) 540 if cbm.size > 0 { 541 cbm._map = (*C.uint8_t)(C.malloc(C.ulong(cbm.size) * C.sizeof_uint8_t)) 542 cs := (*[1 << 31]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] 543 544 copy(cs, bm.bitmap) 545 } 546 547 return nil 548} 549 550func (sr ShutdownReason) String() (str string) { 551 cstr := C.libxl_shutdown_reason_to_string(C.libxl_shutdown_reason(sr)) 552 str = C.GoString(cstr) 553 554 return 555} 556 557func (dt DomainType) String() (str string) { 558 cstr := C.libxl_domain_type_to_string(C.libxl_domain_type(dt)) 559 str = C.GoString(cstr) 560 561 return 562} 563 564// const char *libxl_scheduler_to_string(libxl_scheduler p); 565 566func (s Scheduler) String() string { 567 cs := C.libxl_scheduler_to_string(C.libxl_scheduler(s)) 568 // No need to free const return value 569 570 return C.GoString(cs) 571} 572 573// int libxl_scheduler_from_string(const char *s, libxl_scheduler *e); 574 575func (s *Scheduler) FromString(gstr string) (err error) { 576 *s, err = SchedulerFromString(gstr) 577 return 578} 579 580func SchedulerFromString(name string) (s Scheduler, err error) { 581 cname := C.CString(name) 582 defer C.free(unsafe.Pointer(cname)) 583 584 var cs C.libxl_scheduler 585 586 ret := C.libxl_scheduler_from_string(cname, &cs) 587 if ret != 0 { 588 err = Error(ret) 589 return 590 } 591 592 s = Scheduler(cs) 593 594 return 595} 596 597// libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out); 598// void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool); 599 600func (ctx *Context) ListCpupool() (list []Cpupoolinfo) { 601 var nbPool C.int 602 603 c_cpupool_list := C.libxl_list_cpupool(ctx.ctx, &nbPool) 604 605 defer C.libxl_cpupoolinfo_list_free(c_cpupool_list, nbPool) 606 607 if int(nbPool) == 0 { 608 return 609 } 610 611 // Magic 612 cpupoolListSlice := (*[1 << 30]C.libxl_cpupoolinfo)(unsafe.Pointer(c_cpupool_list))[:nbPool:nbPool] 613 for i := range cpupoolListSlice { 614 var info Cpupoolinfo 615 _ = info.fromC(&cpupoolListSlice[i]) 616 list = append(list, info) 617 } 618 619 return 620} 621 622// int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid); 623 624func (ctx *Context) CpupoolInfo(Poolid uint32) (pool Cpupoolinfo, err error) { 625 var c_cpupool C.libxl_cpupoolinfo 626 627 ret := C.libxl_cpupool_info(ctx.ctx, &c_cpupool, C.uint32_t(Poolid)) 628 if ret != 0 { 629 err = Error(ret) 630 return 631 } 632 defer C.libxl_cpupoolinfo_dispose(&c_cpupool) 633 634 err = pool.fromC(&c_cpupool) 635 636 return 637} 638 639// int libxl_cpupool_create(libxl_ctx *ctx, const char *name, 640// libxl_scheduler sched, 641// libxl_bitmap cpumap, libxl_uuid *uuid, 642// uint32_t *poolid); 643// FIXME: uuid 644// FIXME: Setting poolid 645 646func (ctx *Context) CpupoolCreate(Name string, Scheduler Scheduler, Cpumap Bitmap) (err error, Poolid uint32) { 647 poolid := C.uint32_t(C.LIBXL_CPUPOOL_POOLID_ANY) 648 name := C.CString(Name) 649 defer C.free(unsafe.Pointer(name)) 650 651 // For now, just do what xl does, and make a new uuid every time we create the pool 652 var uuid C.libxl_uuid 653 C.libxl_uuid_generate(&uuid) 654 655 var cbm C.libxl_bitmap 656 if err = Cpumap.toC(&cbm); err != nil { 657 return 658 } 659 defer C.libxl_bitmap_dispose(&cbm) 660 661 ret := C.libxl_cpupool_create(ctx.ctx, name, C.libxl_scheduler(Scheduler), 662 cbm, &uuid, &poolid) 663 if ret != 0 { 664 err = Error(ret) 665 return 666 } 667 668 Poolid = uint32(poolid) 669 670 return 671} 672 673// int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid); 674 675func (ctx *Context) CpupoolDestroy(Poolid uint32) (err error) { 676 ret := C.libxl_cpupool_destroy(ctx.ctx, C.uint32_t(Poolid)) 677 if ret != 0 { 678 err = Error(ret) 679 return 680 } 681 682 return 683} 684 685// int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu); 686 687func (ctx *Context) CpupoolCpuadd(Poolid uint32, Cpu int) (err error) { 688 ret := C.libxl_cpupool_cpuadd(ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) 689 if ret != 0 { 690 err = Error(ret) 691 return 692 } 693 694 return 695} 696 697// int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid, 698// const libxl_bitmap *cpumap); 699 700func (ctx *Context) CpupoolCpuaddCpumap(Poolid uint32, Cpumap Bitmap) (err error) { 701 var cbm C.libxl_bitmap 702 if err = Cpumap.toC(&cbm); err != nil { 703 return 704 } 705 defer C.libxl_bitmap_dispose(&cbm) 706 707 ret := C.libxl_cpupool_cpuadd_cpumap(ctx.ctx, C.uint32_t(Poolid), &cbm) 708 if ret != 0 { 709 err = Error(ret) 710 return 711 } 712 713 return 714} 715 716// int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu); 717 718func (ctx *Context) CpupoolCpuremove(Poolid uint32, Cpu int) (err error) { 719 ret := C.libxl_cpupool_cpuremove(ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) 720 if ret != 0 { 721 err = Error(ret) 722 return 723 } 724 725 return 726} 727 728// int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid, 729// const libxl_bitmap *cpumap); 730 731func (ctx *Context) CpupoolCpuremoveCpumap(Poolid uint32, Cpumap Bitmap) (err error) { 732 var cbm C.libxl_bitmap 733 if err = Cpumap.toC(&cbm); err != nil { 734 return 735 } 736 defer C.libxl_bitmap_dispose(&cbm) 737 738 ret := C.libxl_cpupool_cpuremove_cpumap(ctx.ctx, C.uint32_t(Poolid), &cbm) 739 if ret != 0 { 740 err = Error(ret) 741 return 742 } 743 744 return 745} 746 747// int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid); 748 749func (ctx *Context) CpupoolRename(Name string, Poolid uint32) (err error) { 750 name := C.CString(Name) 751 defer C.free(unsafe.Pointer(name)) 752 753 ret := C.libxl_cpupool_rename(ctx.ctx, name, C.uint32_t(Poolid)) 754 if ret != 0 { 755 err = Error(ret) 756 return 757 } 758 759 return 760} 761 762// int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); 763 764func (ctx *Context) CpupoolCpuaddNode(Poolid uint32, Node int) (Cpus int, err error) { 765 ccpus := C.int(0) 766 767 ret := C.libxl_cpupool_cpuadd_node(ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) 768 if ret != 0 { 769 err = Error(ret) 770 return 771 } 772 773 Cpus = int(ccpus) 774 775 return 776} 777 778// int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); 779 780func (ctx *Context) CpupoolCpuremoveNode(Poolid uint32, Node int) (Cpus int, err error) { 781 ccpus := C.int(0) 782 783 ret := C.libxl_cpupool_cpuremove_node(ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) 784 if ret != 0 { 785 err = Error(ret) 786 return 787 } 788 789 Cpus = int(ccpus) 790 791 return 792} 793 794// int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid); 795 796func (ctx *Context) CpupoolMovedomain(Poolid uint32, Id Domid) (err error) { 797 ret := C.libxl_cpupool_movedomain(ctx.ctx, C.uint32_t(Poolid), C.uint32_t(Id)) 798 if ret != 0 { 799 err = Error(ret) 800 return 801 } 802 803 return 804} 805 806// 807// Utility functions 808// 809 810func (ctx *Context) CpupoolFindByName(name string) (info Cpupoolinfo, found bool) { 811 plist := ctx.ListCpupool() 812 813 for i := range plist { 814 if plist[i].PoolName == name { 815 found = true 816 info = plist[i] 817 return 818 } 819 } 820 return 821} 822 823func (ctx *Context) CpupoolMakeFree(Cpumap Bitmap) (err error) { 824 plist := ctx.ListCpupool() 825 826 for i := range plist { 827 var Intersection Bitmap 828 Intersection = Cpumap.And(plist[i].Cpumap) 829 if !Intersection.IsEmpty() { 830 err = ctx.CpupoolCpuremoveCpumap(plist[i].Poolid, Intersection) 831 if err != nil { 832 return 833 } 834 } 835 } 836 return 837} 838 839/* 840 * Bitmap operations 841 */ 842 843func (bm *Bitmap) Test(bit int) bool { 844 ubit := uint(bit) 845 if bit > bm.Max() || bm.bitmap == nil { 846 return false 847 } 848 849 return (bm.bitmap[bit/8] & (1 << (ubit & 7))) != 0 850} 851 852func (bm *Bitmap) Set(bit int) { 853 ibit := bit / 8 854 if ibit+1 > len(bm.bitmap) { 855 bm.bitmap = append(bm.bitmap, make([]C.uint8_t, ibit+1-len(bm.bitmap))...) 856 } 857 858 bm.bitmap[ibit] |= 1 << (uint(bit) & 7) 859} 860 861func (bm *Bitmap) SetRange(start int, end int) { 862 for i := start; i <= end; i++ { 863 bm.Set(i) 864 } 865} 866 867func (bm *Bitmap) Clear(bit int) { 868 ubit := uint(bit) 869 if bit > bm.Max() || bm.bitmap == nil { 870 return 871 } 872 873 bm.bitmap[bit/8] &= ^(1 << (ubit & 7)) 874} 875 876func (bm *Bitmap) ClearRange(start int, end int) { 877 for i := start; i <= end; i++ { 878 bm.Clear(i) 879 } 880} 881 882func (bm *Bitmap) Max() int { 883 return len(bm.bitmap)*8 - 1 884} 885 886func (bm *Bitmap) IsEmpty() bool { 887 for i := 0; i < len(bm.bitmap); i++ { 888 if bm.bitmap[i] != 0 { 889 return false 890 } 891 } 892 return true 893} 894 895func (a Bitmap) And(b Bitmap) (c Bitmap) { 896 var max, min int 897 if len(a.bitmap) > len(b.bitmap) { 898 max = len(a.bitmap) 899 min = len(b.bitmap) 900 } else { 901 max = len(b.bitmap) 902 min = len(a.bitmap) 903 } 904 c.bitmap = make([]C.uint8_t, max) 905 906 for i := 0; i < min; i++ { 907 c.bitmap[i] = a.bitmap[i] & b.bitmap[i] 908 } 909 return 910} 911 912func (bm Bitmap) String() (s string) { 913 lastOnline := false 914 crange := false 915 printed := false 916 var i int 917 /// --x-xxxxx-x -> 2,4-8,10 918 /// --x-xxxxxxx -> 2,4-10 919 for i = 0; i <= bm.Max(); i++ { 920 if bm.Test(i) { 921 if !lastOnline { 922 // Switching offline -> online, print this cpu 923 if printed { 924 s += "," 925 } 926 s += fmt.Sprintf("%d", i) 927 printed = true 928 } else if !crange { 929 // last was online, but we're not in a range; print - 930 crange = true 931 s += "-" 932 } else { 933 // last was online, we're in a range, nothing else to do 934 } 935 lastOnline = true 936 } else { 937 if lastOnline { 938 // Switching online->offline; do we need to end a range? 939 if crange { 940 s += fmt.Sprintf("%d", i-1) 941 } 942 } 943 lastOnline = false 944 crange = false 945 } 946 } 947 if lastOnline { 948 // Switching online->offline; do we need to end a range? 949 if crange { 950 s += fmt.Sprintf("%d", i-1) 951 } 952 } 953 954 return 955} 956 957//int libxl_get_max_cpus(libxl_ctx *ctx); 958 959func (ctx *Context) GetMaxCpus() (maxCpus int, err error) { 960 ret := C.libxl_get_max_cpus(ctx.ctx) 961 if ret < 0 { 962 err = Error(ret) 963 return 964 } 965 maxCpus = int(ret) 966 return 967} 968 969//int libxl_get_online_cpus(libxl_ctx *ctx); 970 971func (ctx *Context) GetOnlineCpus() (onCpus int, err error) { 972 ret := C.libxl_get_online_cpus(ctx.ctx) 973 if ret < 0 { 974 err = Error(ret) 975 return 976 } 977 onCpus = int(ret) 978 return 979} 980 981//int libxl_get_max_nodes(libxl_ctx *ctx); 982 983func (ctx *Context) GetMaxNodes() (maxNodes int, err error) { 984 ret := C.libxl_get_max_nodes(ctx.ctx) 985 if ret < 0 { 986 err = Error(ret) 987 return 988 } 989 maxNodes = int(ret) 990 return 991} 992 993//int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb); 994 995func (ctx *Context) GetFreeMemory() (memkb uint64, err error) { 996 var cmem C.uint64_t 997 ret := C.libxl_get_free_memory(ctx.ctx, &cmem) 998 999 if ret < 0 { 1000 err = Error(ret) 1001 return 1002 } 1003 1004 memkb = uint64(cmem) 1005 return 1006 1007} 1008 1009//int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo) 1010 1011func (ctx *Context) GetPhysinfo() (physinfo *Physinfo, err error) { 1012 var cphys C.libxl_physinfo 1013 C.libxl_physinfo_init(&cphys) 1014 defer C.libxl_physinfo_dispose(&cphys) 1015 1016 ret := C.libxl_get_physinfo(ctx.ctx, &cphys) 1017 1018 if ret < 0 { 1019 err = Error(ret) 1020 return 1021 } 1022 physinfo = &Physinfo{} 1023 err = physinfo.fromC(&cphys) 1024 1025 return 1026} 1027 1028//const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx); 1029 1030func (ctx *Context) GetVersionInfo() (info *VersionInfo, err error) { 1031 var cinfo *C.libxl_version_info 1032 1033 cinfo = C.libxl_get_version_info(ctx.ctx) 1034 1035 info = &VersionInfo{} 1036 err = info.fromC(cinfo) 1037 1038 return 1039} 1040 1041func (ctx *Context) DomainInfo(Id Domid) (di *Dominfo, err error) { 1042 var cdi C.libxl_dominfo 1043 C.libxl_dominfo_init(&cdi) 1044 defer C.libxl_dominfo_dispose(&cdi) 1045 1046 ret := C.libxl_domain_info(ctx.ctx, &cdi, C.uint32_t(Id)) 1047 1048 if ret != 0 { 1049 err = Error(ret) 1050 return 1051 } 1052 1053 di = &Dominfo{} 1054 err = di.fromC(&cdi) 1055 1056 return 1057} 1058 1059func (ctx *Context) DomainUnpause(Id Domid) (err error) { 1060 ret := C.libxl_domain_unpause(ctx.ctx, C.uint32_t(Id), nil) 1061 1062 if ret != 0 { 1063 err = Error(ret) 1064 } 1065 return 1066} 1067 1068//int libxl_domain_pause(libxl_ctx *ctx, uint32_t domain); 1069 1070func (ctx *Context) DomainPause(id Domid) (err error) { 1071 ret := C.libxl_domain_pause(ctx.ctx, C.uint32_t(id), nil) 1072 1073 if ret != 0 { 1074 err = Error(ret) 1075 } 1076 return 1077} 1078 1079//int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid); 1080 1081func (ctx *Context) DomainShutdown(id Domid) (err error) { 1082 ret := C.libxl_domain_shutdown(ctx.ctx, C.uint32_t(id), nil) 1083 1084 if ret != 0 { 1085 err = Error(ret) 1086 } 1087 return 1088} 1089 1090//int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid); 1091 1092func (ctx *Context) DomainReboot(id Domid) (err error) { 1093 ret := C.libxl_domain_reboot(ctx.ctx, C.uint32_t(id), nil) 1094 1095 if ret != 0 { 1096 err = Error(ret) 1097 } 1098 return 1099} 1100 1101//libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out); 1102//void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain); 1103 1104func (ctx *Context) ListDomain() (glist []Dominfo) { 1105 var nbDomain C.int 1106 clist := C.libxl_list_domain(ctx.ctx, &nbDomain) 1107 defer C.libxl_dominfo_list_free(clist, nbDomain) 1108 1109 if int(nbDomain) == 0 { 1110 return 1111 } 1112 1113 gslice := (*[1 << 30]C.libxl_dominfo)(unsafe.Pointer(clist))[:nbDomain:nbDomain] 1114 for i := range gslice { 1115 var info Dominfo 1116 _ = info.fromC(&gslice[i]) 1117 glist = append(glist, info) 1118 } 1119 1120 return 1121} 1122 1123//libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, 1124// int *nb_vcpu, int *nr_cpus_out); 1125//void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus); 1126 1127func (ctx *Context) ListVcpu(id Domid) (glist []Vcpuinfo) { 1128 var nbVcpu C.int 1129 var nrCpu C.int 1130 1131 clist := C.libxl_list_vcpu(ctx.ctx, C.uint32_t(id), &nbVcpu, &nrCpu) 1132 defer C.libxl_vcpuinfo_list_free(clist, nbVcpu) 1133 1134 if int(nbVcpu) == 0 { 1135 return 1136 } 1137 1138 gslice := (*[1 << 30]C.libxl_vcpuinfo)(unsafe.Pointer(clist))[:nbVcpu:nbVcpu] 1139 for i := range gslice { 1140 var info Vcpuinfo 1141 _ = info.fromC(&gslice[i]) 1142 glist = append(glist, info) 1143 } 1144 1145 return 1146} 1147 1148func (ct ConsoleType) String() (str string) { 1149 cstr := C.libxl_console_type_to_string(C.libxl_console_type(ct)) 1150 str = C.GoString(cstr) 1151 1152 return 1153} 1154 1155//int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num, 1156//libxl_console_type type, char **path); 1157 1158func (ctx *Context) ConsoleGetTty(id Domid, consNum int, conType ConsoleType) (path string, err error) { 1159 var cpath *C.char 1160 ret := C.libxl_console_get_tty(ctx.ctx, C.uint32_t(id), C.int(consNum), C.libxl_console_type(conType), &cpath) 1161 if ret != 0 { 1162 err = Error(ret) 1163 return 1164 } 1165 defer C.free(unsafe.Pointer(cpath)) 1166 1167 path = C.GoString(cpath) 1168 return 1169} 1170 1171//int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, 1172// char **path); 1173 1174func (ctx *Context) PrimaryConsoleGetTty(domid uint32) (path string, err error) { 1175 var cpath *C.char 1176 ret := C.libxl_primary_console_get_tty(ctx.ctx, C.uint32_t(domid), &cpath) 1177 if ret != 0 { 1178 err = Error(ret) 1179 return 1180 } 1181 defer C.free(unsafe.Pointer(cpath)) 1182 1183 path = C.GoString(cpath) 1184 return 1185} 1186 1187// DeviceNicAdd adds a nic to a domain. 1188func (ctx *Context) DeviceNicAdd(domid Domid, nic *DeviceNic) error { 1189 var cnic C.libxl_device_nic 1190 1191 if err := nic.toC(&cnic); err != nil { 1192 return err 1193 } 1194 defer C.libxl_device_nic_dispose(&cnic) 1195 1196 ret := C.libxl_device_nic_add(ctx.ctx, C.uint32_t(domid), &cnic, nil) 1197 if ret != 0 { 1198 return Error(ret) 1199 } 1200 1201 return nil 1202} 1203 1204// DeviceNicRemove removes a nic from a domain. 1205func (ctx *Context) DeviceNicRemove(domid Domid, nic *DeviceNic) error { 1206 var cnic C.libxl_device_nic 1207 1208 if err := nic.toC(&cnic); err != nil { 1209 return err 1210 } 1211 defer C.libxl_device_nic_dispose(&cnic) 1212 1213 ret := C.libxl_device_nic_remove(ctx.ctx, C.uint32_t(domid), &cnic, nil) 1214 if ret != 0 { 1215 return Error(ret) 1216 } 1217 1218 return nil 1219} 1220 1221// DevicePciAdd is used to passthrough a PCI device to a domain. 1222func (ctx *Context) DevicePciAdd(domid Domid, pci *DevicePci) error { 1223 var cpci C.libxl_device_pci 1224 1225 if err := pci.toC(&cpci); err != nil { 1226 return err 1227 } 1228 defer C.libxl_device_pci_dispose(&cpci) 1229 1230 ret := C.libxl_device_pci_add(ctx.ctx, C.uint32_t(domid), &cpci, nil) 1231 if ret != 0 { 1232 return Error(ret) 1233 } 1234 1235 return nil 1236} 1237 1238// DevicePciRemove removes a PCI device from a domain. 1239func (ctx *Context) DevicePciRemove(domid Domid, pci *DevicePci) error { 1240 var cpci C.libxl_device_pci 1241 1242 if err := pci.toC(&cpci); err != nil { 1243 return err 1244 } 1245 defer C.libxl_device_pci_dispose(&cpci) 1246 1247 ret := C.libxl_device_pci_remove(ctx.ctx, C.uint32_t(domid), &cpci, nil) 1248 if ret != 0 { 1249 return Error(ret) 1250 } 1251 1252 return nil 1253} 1254 1255// DeviceUsbdevAdd adds a USB device to a domain. 1256func (ctx *Context) DeviceUsbdevAdd(domid Domid, usbdev *DeviceUsbdev) error { 1257 var cusbdev C.libxl_device_usbdev 1258 1259 if err := usbdev.toC(&cusbdev); err != nil { 1260 return err 1261 } 1262 defer C.libxl_device_usbdev_dispose(&cusbdev) 1263 1264 ret := C.libxl_device_usbdev_add(ctx.ctx, C.uint32_t(domid), &cusbdev, nil) 1265 if ret != 0 { 1266 return Error(ret) 1267 } 1268 1269 return nil 1270} 1271 1272// DeviceUsbdevRemove removes a USB device from a domain. 1273func (ctx *Context) DeviceUsbdevRemove(domid Domid, usbdev *DeviceUsbdev) error { 1274 var cusbdev C.libxl_device_usbdev 1275 1276 if err := usbdev.toC(&cusbdev); err != nil { 1277 return err 1278 } 1279 defer C.libxl_device_usbdev_dispose(&cusbdev) 1280 1281 ret := C.libxl_device_usbdev_remove(ctx.ctx, C.uint32_t(domid), &cusbdev, nil) 1282 if ret != 0 { 1283 return Error(ret) 1284 } 1285 1286 return nil 1287} 1288 1289// DomainCreateNew creates a new domain. 1290func (ctx *Context) DomainCreateNew(config *DomainConfig) (Domid, error) { 1291 var cdomid C.uint32_t 1292 var cconfig C.libxl_domain_config 1293 err := config.toC(&cconfig) 1294 if err != nil { 1295 return Domid(0), fmt.Errorf("converting domain config to C: %v", err) 1296 } 1297 defer C.libxl_domain_config_dispose(&cconfig) 1298 1299 ret := C.libxl_domain_create_new(ctx.ctx, &cconfig, &cdomid, nil, nil) 1300 if ret != 0 { 1301 return Domid(0), Error(ret) 1302 } 1303 1304 return Domid(cdomid), nil 1305} 1306 1307// DomainDestroy destroys a domain given a domid. 1308func (ctx *Context) DomainDestroy(domid Domid) error { 1309 ret := C.libxl_domain_destroy(ctx.ctx, C.uint32_t(domid), nil) 1310 if ret != 0 { 1311 return Error(ret) 1312 } 1313 1314 return nil 1315} 1316 1317// SendTrigger sends a Trigger to the domain specified by domid. 1318func (ctx *Context) SendTrigger(domid Domid, trigger Trigger, vcpuid int) error { 1319 ret := C.libxl_send_trigger(ctx.ctx, C.uint32_t(domid), 1320 C.libxl_trigger(trigger), C.uint32_t(vcpuid), nil) 1321 if ret != 0 { 1322 return Error(ret) 1323 } 1324 1325 return nil 1326} 1327