/* * Copyright (C) 2016 George W. Dunlap, Citrix Systems UK Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ // Package xenlight provides bindings to Xen's libxl C library. package xenlight /* #cgo LDFLAGS: -lxenlight -lyajl -lxentoollog #include #include #include #define INVALID_DOMID_TYPED ((uint32_t) INVALID_DOMID) static const libxl_childproc_hooks childproc_hooks = { .chldowner = libxl_sigchld_owner_mainloop }; void xenlight_set_chldproc(libxl_ctx *ctx) { libxl_childproc_setmode(ctx, &childproc_hooks, NULL); } */ import "C" /* * Other flags that may be needed at some point: * -lnl-route-3 -lnl-3 * * To get back to static linking: * #cgo LDFLAGS: -lxenlight -lyajl_s -lxengnttab -lxenstore -lxenguest -lxentoollog -lxenevtchn -lxenctrl -lxenforeignmemory -lxencall -lz -luuid -lutil */ import ( "fmt" "os" "os/signal" "syscall" "unsafe" ) var libxlErrors = map[Error]string{ ErrorNonspecific: "Non-specific error", ErrorVersion: "Wrong version", ErrorFail: "Failed", ErrorNi: "Not Implemented", ErrorNomem: "No memory", ErrorInval: "Invalid argument", ErrorBadfail: "Bad Fail", ErrorGuestTimedout: "Guest timed out", ErrorTimedout: "Timed out", ErrorNoparavirt: "No Paravirtualization", ErrorNotReady: "Not ready", ErrorOseventRegFail: "OS event registration failed", ErrorBufferfull: "Buffer full", ErrorUnknownChild: "Unknown child", ErrorLockFail: "Lock failed", ErrorJsonConfigEmpty: "JSON config empty", ErrorDeviceExists: "Device exists", ErrorCheckpointDevopsDoesNotMatch: "Checkpoint devops does not match", ErrorCheckpointDeviceNotSupported: "Checkpoint device not supported", ErrorVnumaConfigInvalid: "VNUMA config invalid", ErrorDomainNotfound: "Domain not found", ErrorAborted: "Aborted", ErrorNotfound: "Not found", ErrorDomainDestroyed: "Domain destroyed", ErrorFeatureRemoved: "Feature removed", } const ( DomidInvalid Domid = Domid(C.INVALID_DOMID_TYPED) ) func (e Error) Error() string { if s, ok := libxlErrors[e]; ok { return s } return fmt.Sprintf("libxl error: %d", e) } // Context represents a libxl_ctx. type Context struct { ctx *C.libxl_ctx logger *C.xentoollog_logger_stdiostream sigchld chan os.Signal sigchldDone chan struct{} } // Golang always unmasks SIGCHLD, and internally has ways of // distributing SIGCHLD to multiple recipients. libxl has provision // for this model: just tell it when a SIGCHLD happened, and it will // look after its own processes. // // This should "play nicely" with other users of SIGCHLD as long as // they don't reap libxl's processes. // // Every context needs to be notified on each SIGCHLD; so spin up a // new goroutine for each context. If there are a large number of // contexts, this means each context will be woken up looking through // its own list of children. // // The alternate would be to register a fork callback, such that the // xenlight package can make a single list of all children, and only // notify the specific libxl context(s) that have children woken. But // it's not clear to me this will be much more work than having the // xenlight go library do the same thing; doing it in separate go // threads has the potential to do it in parallel. Leave that as an // optimization for later if it turns out to be a bottleneck. func sigchldHandler(ctx *Context) { for _ = range ctx.sigchld { C.libxl_childproc_sigchld_occurred(ctx.ctx) } close(ctx.sigchldDone) } // NewContext returns a new Context. func NewContext() (ctx *Context, err error) { ctx = &Context{} defer func() { if err != nil { ctx.Close() ctx = nil } }() // Create a logger ctx.logger = C.xtl_createlogger_stdiostream(C.stderr, C.XTL_ERROR, 0) // Allocate a context ret := C.libxl_ctx_alloc(&ctx.ctx, C.LIBXL_VERSION, 0, (*C.xentoollog_logger)(unsafe.Pointer(ctx.logger))) if ret != 0 { return ctx, Error(ret) } // Tell libxl that we'll be dealing with SIGCHLD... C.xenlight_set_chldproc(ctx.ctx) // ...and arrange to keep that promise. ctx.sigchld = make(chan os.Signal, 2) ctx.sigchldDone = make(chan struct{}, 1) signal.Notify(ctx.sigchld, syscall.SIGCHLD) // This goroutine will run until the ctx.sigchld is closed in // ctx.Close(); at which point it will close ctx.sigchldDone. go sigchldHandler(ctx) return ctx, nil } // Close closes the Context. func (ctx *Context) Close() error { // Tell our SIGCHLD notifier to shut down, and wait for it to exit // before we free the context. if ctx.sigchld != nil { signal.Stop(ctx.sigchld) close(ctx.sigchld) <-ctx.sigchldDone ctx.sigchld = nil ctx.sigchldDone = nil } if ctx.ctx != nil { ret := C.libxl_ctx_free(ctx.ctx) if ret != 0 { return Error(ret) } ctx.ctx = nil } if ctx.logger != nil { C.xtl_logger_destroy((*C.xentoollog_logger)(unsafe.Pointer(ctx.logger))) ctx.logger = nil } return nil } /* * Types: Builtins */ type Domid uint32 // NameToDomid returns the Domid for a domain, given its name, if it exists. // // NameToDomid does not guarantee that the domid associated with name at // the time NameToDomid is called is the same as the domid associated with // name at the time NameToDomid returns. func (ctx *Context) NameToDomid(name string) (Domid, error) { var domid C.uint32_t cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) if ret := C.libxl_name_to_domid(ctx.ctx, cname, &domid); ret != 0 { return DomidInvalid, Error(ret) } return Domid(domid), nil } // DomidToName returns the name for a domain, given its domid. If there // is no domain with the given domid, DomidToName will return the empty // string. // // DomidToName does not guarantee that the name (if any) associated with domid // at the time DomidToName is called is the same as the name (if any) associated // with domid at the time DomidToName returns. func (ctx *Context) DomidToName(domid Domid) string { cname := C.libxl_domid_to_name(ctx.ctx, C.uint32_t(domid)) defer C.free(unsafe.Pointer(cname)) return C.GoString(cname) } // Devid is a device ID. type Devid int // Uuid is a domain UUID. type Uuid [16]byte // String formats a Uuid in the form "xxxx-xx-xx-xx-xxxxxx". func (u Uuid) String() string { s := "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x" opts := make([]interface{}, 16) for i, v := range u { opts[i] = v } return fmt.Sprintf(s, opts...) } func (u *Uuid) fromC(c *C.libxl_uuid) error { for i := range *u { u[i] = byte(c.uuid[i]) } return nil } func (u *Uuid) toC(cu *C.libxl_uuid) error { for i, v := range u { cu.uuid[i] = C.uint8_t(v) } return nil } // defboolVal represents a defbool value. type defboolVal int const ( defboolDefault defboolVal = 0 defboolFalse defboolVal = -1 defboolTrue defboolVal = 1 ) // Defbool represents a libxl_defbool. type Defbool struct { val defboolVal } func (d Defbool) String() string { switch d.val { case defboolDefault: return "" case defboolFalse: return "False" case defboolTrue: return "True" } return "" } // Set sets the value of the Defbool. func (d *Defbool) Set(b bool) { if b { d.val = defboolTrue return } d.val = defboolFalse } // Unset resets the Defbool to default value. func (d *Defbool) Unset() { d.val = defboolDefault } // SetIfDefault sets the value of Defbool only if // its current value is default. func (d *Defbool) SetIfDefault(b bool) { if d.IsDefault() { d.Set(b) } } // IsDefault returns true if the value of Defbool // is default, returns false otherwise. func (d *Defbool) IsDefault() bool { return d.val == defboolDefault } // Val returns the boolean value associated with the // Defbool value. An error is returned if the value // is default. func (d *Defbool) Val() (bool, error) { if d.IsDefault() { return false, fmt.Errorf("%v: cannot take value of default defbool", ErrorInval) } return (d.val > 0), nil } func (d *Defbool) fromC(c *C.libxl_defbool) error { if C.libxl_defbool_is_default(*c) { d.val = defboolDefault return nil } if C.libxl_defbool_val(*c) { d.val = defboolTrue return nil } d.val = defboolFalse return nil } func (d *Defbool) toC(cd *C.libxl_defbool) error { if !d.IsDefault() { val, _ := d.Val() C.libxl_defbool_set(cd, C.bool(val)) } return nil } // Mac represents a libxl_mac, or simply a MAC address. type Mac [6]byte // String formats a Mac address to string representation. func (mac Mac) String() string { s := "%02x:%02x:%02x:%02x:%02x:%02x" opts := make([]interface{}, 6) for i, v := range mac { opts[i] = v } return fmt.Sprintf(s, opts...) } func (mac *Mac) fromC(cmac *C.libxl_mac) error { for i := range *mac { mac[i] = byte(cmac[i]) } return nil } func (mac Mac) toC(cm *C.libxl_mac) error { for i, v := range mac { (*cm)[i] = C.uint8_t(v) } return nil } // MsVmGenid represents a libxl_ms_vm_genid. type MsVmGenid [int(C.LIBXL_MS_VM_GENID_LEN)]byte func (mvg *MsVmGenid) fromC(cmvg *C.libxl_ms_vm_genid) error { for i := range *mvg { mvg[i] = byte(cmvg.bytes[i]) } return nil } func (mvg *MsVmGenid) toC(cmvg *C.libxl_ms_vm_genid) error { for i, v := range mvg { cmvg.bytes[i] = C.uint8_t(v) } return nil } // EvLink represents a libxl_ev_link. // // Represented as an empty struct for now, as there is no // apparent need for the internals of this type to be exposed // through the Go package. type EvLink struct{} func (el *EvLink) fromC(cel *C.libxl_ev_link) error { return nil } func (el *EvLink) toC(cel *C.libxl_ev_link) (err error) { return } // CpuidPolicyList represents a libxl_cpuid_policy_list. // // The value of CpuidPolicyList is honored when used as input to libxl. If // a struct contains a field of type CpuidPolicyList, that field will be left // empty when it is returned from libxl. type CpuidPolicyList string func (cpl *CpuidPolicyList) fromC(ccpl *C.libxl_cpuid_policy_list) error { *cpl = ""; return nil } func (cpl CpuidPolicyList) toC(ccpl *C.libxl_cpuid_policy_list) error { if cpl == "" { *ccpl = nil return nil } s := C.CString(string(cpl)) defer C.free(unsafe.Pointer(s)) ret := C.libxl_cpuid_parse_config(ccpl, s) if ret != 0 { C.libxl_cpuid_dispose(ccpl) // libxl_cpuid_parse_config doesn't return a normal libxl error. return ErrorInval } return nil } // Hwcap represents a libxl_hwcap. type Hwcap [8]uint32 func (hwcap *Hwcap) fromC(chwcap *C.libxl_hwcap) error { for i := range *hwcap { hwcap[i] = uint32(chwcap[i]) } return nil } func (hwcap *Hwcap) toC(chwcap *C.libxl_hwcap) error { for i, v := range hwcap { (*chwcap)[i] = C.uint32_t(v) } return nil } // KeyValueList represents a libxl_key_value_list. // // Represented as an empty struct for now, as there is no // apparent need for this type to be exposed through the // Go package. type KeyValueList struct{} func (kvl KeyValueList) fromC(ckvl *C.libxl_key_value_list) error { return nil } func (kvl KeyValueList) toC(ckvl *C.libxl_key_value_list) (err error) { return } // StringList represents a libxl_string_list. type StringList []string func (sl *StringList) fromC(csl *C.libxl_string_list) error { size := int(C.libxl_string_list_length(csl)) list := (*[1 << 30]*C.char)(unsafe.Pointer(csl))[:size:size] *sl = make([]string, size) for i, v := range list { (*sl)[i] = C.GoString(v) } return nil } func (sl StringList) toC(csl *C.libxl_string_list) error { var char *C.char size := len(sl) + 1 *csl = (C.libxl_string_list)(C.malloc(C.ulong(size) * C.ulong(unsafe.Sizeof(char)))) clist := (*[1 << 30]*C.char)(unsafe.Pointer(*csl))[:size:size] for i, v := range sl { clist[i] = C.CString(v) } clist[len(clist)-1] = nil return nil } // Bitmap represents a libxl_bitmap. // // Implement the Go bitmap type such that the underlying data can // easily be copied in and out. NB that we still have to do copies // both directions, because cgo runtime restrictions forbid passing to // a C function a pointer to a Go-allocated structure which contains a // pointer. type Bitmap struct { // typedef struct { // uint32_t size; /* number of bytes in map */ // uint8_t *map; // } libxl_bitmap; bitmap []C.uint8_t } func (bm *Bitmap) fromC(cbm *C.libxl_bitmap) error { bm.bitmap = nil if size := int(cbm.size); size > 0 { // Alloc a Go slice for the bytes bm.bitmap = make([]C.uint8_t, size) // Make a slice pointing to the C array cs := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] // And copy the C array into the Go array copy(bm.bitmap, cs) } return nil } func (bm *Bitmap) toC(cbm *C.libxl_bitmap) error { size := len(bm.bitmap) cbm.size = C.uint32_t(size) if cbm.size > 0 { cbm._map = (*C.uint8_t)(C.malloc(C.ulong(cbm.size) * C.sizeof_uint8_t)) cs := (*[1 << 31]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] copy(cs, bm.bitmap) } return nil } func (sr ShutdownReason) String() (str string) { cstr := C.libxl_shutdown_reason_to_string(C.libxl_shutdown_reason(sr)) str = C.GoString(cstr) return } func (dt DomainType) String() (str string) { cstr := C.libxl_domain_type_to_string(C.libxl_domain_type(dt)) str = C.GoString(cstr) return } // const char *libxl_scheduler_to_string(libxl_scheduler p); func (s Scheduler) String() string { cs := C.libxl_scheduler_to_string(C.libxl_scheduler(s)) // No need to free const return value return C.GoString(cs) } // int libxl_scheduler_from_string(const char *s, libxl_scheduler *e); func (s *Scheduler) FromString(gstr string) (err error) { *s, err = SchedulerFromString(gstr) return } func SchedulerFromString(name string) (s Scheduler, err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) var cs C.libxl_scheduler ret := C.libxl_scheduler_from_string(cname, &cs) if ret != 0 { err = Error(ret) return } s = Scheduler(cs) return } // libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out); // void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool); func (ctx *Context) ListCpupool() (list []Cpupoolinfo) { var nbPool C.int c_cpupool_list := C.libxl_list_cpupool(ctx.ctx, &nbPool) defer C.libxl_cpupoolinfo_list_free(c_cpupool_list, nbPool) if int(nbPool) == 0 { return } // Magic cpupoolListSlice := (*[1 << 30]C.libxl_cpupoolinfo)(unsafe.Pointer(c_cpupool_list))[:nbPool:nbPool] for i := range cpupoolListSlice { var info Cpupoolinfo _ = info.fromC(&cpupoolListSlice[i]) list = append(list, info) } return } // int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid); func (ctx *Context) CpupoolInfo(Poolid uint32) (pool Cpupoolinfo, err error) { var c_cpupool C.libxl_cpupoolinfo ret := C.libxl_cpupool_info(ctx.ctx, &c_cpupool, C.uint32_t(Poolid)) if ret != 0 { err = Error(ret) return } defer C.libxl_cpupoolinfo_dispose(&c_cpupool) err = pool.fromC(&c_cpupool) return } // int libxl_cpupool_create(libxl_ctx *ctx, const char *name, // libxl_scheduler sched, // libxl_bitmap cpumap, libxl_uuid *uuid, // uint32_t *poolid); // FIXME: uuid // FIXME: Setting poolid func (ctx *Context) CpupoolCreate(Name string, Scheduler Scheduler, Cpumap Bitmap) (err error, Poolid uint32) { poolid := C.uint32_t(C.LIBXL_CPUPOOL_POOLID_ANY) name := C.CString(Name) defer C.free(unsafe.Pointer(name)) // For now, just do what xl does, and make a new uuid every time we create the pool var uuid C.libxl_uuid C.libxl_uuid_generate(&uuid) var cbm C.libxl_bitmap if err = Cpumap.toC(&cbm); err != nil { return } defer C.libxl_bitmap_dispose(&cbm) ret := C.libxl_cpupool_create(ctx.ctx, name, C.libxl_scheduler(Scheduler), cbm, &uuid, &poolid) if ret != 0 { err = Error(ret) return } Poolid = uint32(poolid) return } // int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid); func (ctx *Context) CpupoolDestroy(Poolid uint32) (err error) { ret := C.libxl_cpupool_destroy(ctx.ctx, C.uint32_t(Poolid)) if ret != 0 { err = Error(ret) return } return } // int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu); func (ctx *Context) CpupoolCpuadd(Poolid uint32, Cpu int) (err error) { ret := C.libxl_cpupool_cpuadd(ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) if ret != 0 { err = Error(ret) return } return } // int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid, // const libxl_bitmap *cpumap); func (ctx *Context) CpupoolCpuaddCpumap(Poolid uint32, Cpumap Bitmap) (err error) { var cbm C.libxl_bitmap if err = Cpumap.toC(&cbm); err != nil { return } defer C.libxl_bitmap_dispose(&cbm) ret := C.libxl_cpupool_cpuadd_cpumap(ctx.ctx, C.uint32_t(Poolid), &cbm) if ret != 0 { err = Error(ret) return } return } // int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu); func (ctx *Context) CpupoolCpuremove(Poolid uint32, Cpu int) (err error) { ret := C.libxl_cpupool_cpuremove(ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) if ret != 0 { err = Error(ret) return } return } // int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid, // const libxl_bitmap *cpumap); func (ctx *Context) CpupoolCpuremoveCpumap(Poolid uint32, Cpumap Bitmap) (err error) { var cbm C.libxl_bitmap if err = Cpumap.toC(&cbm); err != nil { return } defer C.libxl_bitmap_dispose(&cbm) ret := C.libxl_cpupool_cpuremove_cpumap(ctx.ctx, C.uint32_t(Poolid), &cbm) if ret != 0 { err = Error(ret) return } return } // int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid); func (ctx *Context) CpupoolRename(Name string, Poolid uint32) (err error) { name := C.CString(Name) defer C.free(unsafe.Pointer(name)) ret := C.libxl_cpupool_rename(ctx.ctx, name, C.uint32_t(Poolid)) if ret != 0 { err = Error(ret) return } return } // int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); func (ctx *Context) CpupoolCpuaddNode(Poolid uint32, Node int) (Cpus int, err error) { ccpus := C.int(0) ret := C.libxl_cpupool_cpuadd_node(ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) if ret != 0 { err = Error(ret) return } Cpus = int(ccpus) return } // int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); func (ctx *Context) CpupoolCpuremoveNode(Poolid uint32, Node int) (Cpus int, err error) { ccpus := C.int(0) ret := C.libxl_cpupool_cpuremove_node(ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) if ret != 0 { err = Error(ret) return } Cpus = int(ccpus) return } // int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid); func (ctx *Context) CpupoolMovedomain(Poolid uint32, Id Domid) (err error) { ret := C.libxl_cpupool_movedomain(ctx.ctx, C.uint32_t(Poolid), C.uint32_t(Id)) if ret != 0 { err = Error(ret) return } return } // // Utility functions // func (ctx *Context) CpupoolFindByName(name string) (info Cpupoolinfo, found bool) { plist := ctx.ListCpupool() for i := range plist { if plist[i].PoolName == name { found = true info = plist[i] return } } return } func (ctx *Context) CpupoolMakeFree(Cpumap Bitmap) (err error) { plist := ctx.ListCpupool() for i := range plist { var Intersection Bitmap Intersection = Cpumap.And(plist[i].Cpumap) if !Intersection.IsEmpty() { err = ctx.CpupoolCpuremoveCpumap(plist[i].Poolid, Intersection) if err != nil { return } } } return } /* * Bitmap operations */ func (bm *Bitmap) Test(bit int) bool { ubit := uint(bit) if bit > bm.Max() || bm.bitmap == nil { return false } return (bm.bitmap[bit/8] & (1 << (ubit & 7))) != 0 } func (bm *Bitmap) Set(bit int) { ibit := bit / 8 if ibit+1 > len(bm.bitmap) { bm.bitmap = append(bm.bitmap, make([]C.uint8_t, ibit+1-len(bm.bitmap))...) } bm.bitmap[ibit] |= 1 << (uint(bit) & 7) } func (bm *Bitmap) SetRange(start int, end int) { for i := start; i <= end; i++ { bm.Set(i) } } func (bm *Bitmap) Clear(bit int) { ubit := uint(bit) if bit > bm.Max() || bm.bitmap == nil { return } bm.bitmap[bit/8] &= ^(1 << (ubit & 7)) } func (bm *Bitmap) ClearRange(start int, end int) { for i := start; i <= end; i++ { bm.Clear(i) } } func (bm *Bitmap) Max() int { return len(bm.bitmap)*8 - 1 } func (bm *Bitmap) IsEmpty() bool { for i := 0; i < len(bm.bitmap); i++ { if bm.bitmap[i] != 0 { return false } } return true } func (a Bitmap) And(b Bitmap) (c Bitmap) { var max, min int if len(a.bitmap) > len(b.bitmap) { max = len(a.bitmap) min = len(b.bitmap) } else { max = len(b.bitmap) min = len(a.bitmap) } c.bitmap = make([]C.uint8_t, max) for i := 0; i < min; i++ { c.bitmap[i] = a.bitmap[i] & b.bitmap[i] } return } func (bm Bitmap) String() (s string) { lastOnline := false crange := false printed := false var i int /// --x-xxxxx-x -> 2,4-8,10 /// --x-xxxxxxx -> 2,4-10 for i = 0; i <= bm.Max(); i++ { if bm.Test(i) { if !lastOnline { // Switching offline -> online, print this cpu if printed { s += "," } s += fmt.Sprintf("%d", i) printed = true } else if !crange { // last was online, but we're not in a range; print - crange = true s += "-" } else { // last was online, we're in a range, nothing else to do } lastOnline = true } else { if lastOnline { // Switching online->offline; do we need to end a range? if crange { s += fmt.Sprintf("%d", i-1) } } lastOnline = false crange = false } } if lastOnline { // Switching online->offline; do we need to end a range? if crange { s += fmt.Sprintf("%d", i-1) } } return } //int libxl_get_max_cpus(libxl_ctx *ctx); func (ctx *Context) GetMaxCpus() (maxCpus int, err error) { ret := C.libxl_get_max_cpus(ctx.ctx) if ret < 0 { err = Error(ret) return } maxCpus = int(ret) return } //int libxl_get_online_cpus(libxl_ctx *ctx); func (ctx *Context) GetOnlineCpus() (onCpus int, err error) { ret := C.libxl_get_online_cpus(ctx.ctx) if ret < 0 { err = Error(ret) return } onCpus = int(ret) return } //int libxl_get_max_nodes(libxl_ctx *ctx); func (ctx *Context) GetMaxNodes() (maxNodes int, err error) { ret := C.libxl_get_max_nodes(ctx.ctx) if ret < 0 { err = Error(ret) return } maxNodes = int(ret) return } //int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb); func (ctx *Context) GetFreeMemory() (memkb uint64, err error) { var cmem C.uint64_t ret := C.libxl_get_free_memory(ctx.ctx, &cmem) if ret < 0 { err = Error(ret) return } memkb = uint64(cmem) return } //int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo) func (ctx *Context) GetPhysinfo() (physinfo *Physinfo, err error) { var cphys C.libxl_physinfo C.libxl_physinfo_init(&cphys) defer C.libxl_physinfo_dispose(&cphys) ret := C.libxl_get_physinfo(ctx.ctx, &cphys) if ret < 0 { err = Error(ret) return } physinfo = &Physinfo{} err = physinfo.fromC(&cphys) return } //const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx); func (ctx *Context) GetVersionInfo() (info *VersionInfo, err error) { var cinfo *C.libxl_version_info cinfo = C.libxl_get_version_info(ctx.ctx) info = &VersionInfo{} err = info.fromC(cinfo) return } func (ctx *Context) DomainInfo(Id Domid) (di *Dominfo, err error) { var cdi C.libxl_dominfo C.libxl_dominfo_init(&cdi) defer C.libxl_dominfo_dispose(&cdi) ret := C.libxl_domain_info(ctx.ctx, &cdi, C.uint32_t(Id)) if ret != 0 { err = Error(ret) return } di = &Dominfo{} err = di.fromC(&cdi) return } func (ctx *Context) DomainUnpause(Id Domid) (err error) { ret := C.libxl_domain_unpause(ctx.ctx, C.uint32_t(Id), nil) if ret != 0 { err = Error(ret) } return } //int libxl_domain_pause(libxl_ctx *ctx, uint32_t domain); func (ctx *Context) DomainPause(id Domid) (err error) { ret := C.libxl_domain_pause(ctx.ctx, C.uint32_t(id), nil) if ret != 0 { err = Error(ret) } return } //int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid); func (ctx *Context) DomainShutdown(id Domid) (err error) { ret := C.libxl_domain_shutdown(ctx.ctx, C.uint32_t(id), nil) if ret != 0 { err = Error(ret) } return } //int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid); func (ctx *Context) DomainReboot(id Domid) (err error) { ret := C.libxl_domain_reboot(ctx.ctx, C.uint32_t(id), nil) if ret != 0 { err = Error(ret) } return } //libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out); //void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain); func (ctx *Context) ListDomain() (glist []Dominfo) { var nbDomain C.int clist := C.libxl_list_domain(ctx.ctx, &nbDomain) defer C.libxl_dominfo_list_free(clist, nbDomain) if int(nbDomain) == 0 { return } gslice := (*[1 << 30]C.libxl_dominfo)(unsafe.Pointer(clist))[:nbDomain:nbDomain] for i := range gslice { var info Dominfo _ = info.fromC(&gslice[i]) glist = append(glist, info) } return } //libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, // int *nb_vcpu, int *nr_cpus_out); //void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus); func (ctx *Context) ListVcpu(id Domid) (glist []Vcpuinfo) { var nbVcpu C.int var nrCpu C.int clist := C.libxl_list_vcpu(ctx.ctx, C.uint32_t(id), &nbVcpu, &nrCpu) defer C.libxl_vcpuinfo_list_free(clist, nbVcpu) if int(nbVcpu) == 0 { return } gslice := (*[1 << 30]C.libxl_vcpuinfo)(unsafe.Pointer(clist))[:nbVcpu:nbVcpu] for i := range gslice { var info Vcpuinfo _ = info.fromC(&gslice[i]) glist = append(glist, info) } return } func (ct ConsoleType) String() (str string) { cstr := C.libxl_console_type_to_string(C.libxl_console_type(ct)) str = C.GoString(cstr) return } //int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num, //libxl_console_type type, char **path); func (ctx *Context) ConsoleGetTty(id Domid, consNum int, conType ConsoleType) (path string, err error) { var cpath *C.char ret := C.libxl_console_get_tty(ctx.ctx, C.uint32_t(id), C.int(consNum), C.libxl_console_type(conType), &cpath) if ret != 0 { err = Error(ret) return } defer C.free(unsafe.Pointer(cpath)) path = C.GoString(cpath) return } //int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, // char **path); func (ctx *Context) PrimaryConsoleGetTty(domid uint32) (path string, err error) { var cpath *C.char ret := C.libxl_primary_console_get_tty(ctx.ctx, C.uint32_t(domid), &cpath) if ret != 0 { err = Error(ret) return } defer C.free(unsafe.Pointer(cpath)) path = C.GoString(cpath) return } // DeviceNicAdd adds a nic to a domain. func (ctx *Context) DeviceNicAdd(domid Domid, nic *DeviceNic) error { var cnic C.libxl_device_nic if err := nic.toC(&cnic); err != nil { return err } defer C.libxl_device_nic_dispose(&cnic) ret := C.libxl_device_nic_add(ctx.ctx, C.uint32_t(domid), &cnic, nil) if ret != 0 { return Error(ret) } return nil } // DeviceNicRemove removes a nic from a domain. func (ctx *Context) DeviceNicRemove(domid Domid, nic *DeviceNic) error { var cnic C.libxl_device_nic if err := nic.toC(&cnic); err != nil { return err } defer C.libxl_device_nic_dispose(&cnic) ret := C.libxl_device_nic_remove(ctx.ctx, C.uint32_t(domid), &cnic, nil) if ret != 0 { return Error(ret) } return nil } // DevicePciAdd is used to passthrough a PCI device to a domain. func (ctx *Context) DevicePciAdd(domid Domid, pci *DevicePci) error { var cpci C.libxl_device_pci if err := pci.toC(&cpci); err != nil { return err } defer C.libxl_device_pci_dispose(&cpci) ret := C.libxl_device_pci_add(ctx.ctx, C.uint32_t(domid), &cpci, nil) if ret != 0 { return Error(ret) } return nil } // DevicePciRemove removes a PCI device from a domain. func (ctx *Context) DevicePciRemove(domid Domid, pci *DevicePci) error { var cpci C.libxl_device_pci if err := pci.toC(&cpci); err != nil { return err } defer C.libxl_device_pci_dispose(&cpci) ret := C.libxl_device_pci_remove(ctx.ctx, C.uint32_t(domid), &cpci, nil) if ret != 0 { return Error(ret) } return nil } // DeviceUsbdevAdd adds a USB device to a domain. func (ctx *Context) DeviceUsbdevAdd(domid Domid, usbdev *DeviceUsbdev) error { var cusbdev C.libxl_device_usbdev if err := usbdev.toC(&cusbdev); err != nil { return err } defer C.libxl_device_usbdev_dispose(&cusbdev) ret := C.libxl_device_usbdev_add(ctx.ctx, C.uint32_t(domid), &cusbdev, nil) if ret != 0 { return Error(ret) } return nil } // DeviceUsbdevRemove removes a USB device from a domain. func (ctx *Context) DeviceUsbdevRemove(domid Domid, usbdev *DeviceUsbdev) error { var cusbdev C.libxl_device_usbdev if err := usbdev.toC(&cusbdev); err != nil { return err } defer C.libxl_device_usbdev_dispose(&cusbdev) ret := C.libxl_device_usbdev_remove(ctx.ctx, C.uint32_t(domid), &cusbdev, nil) if ret != 0 { return Error(ret) } return nil } // DomainCreateNew creates a new domain. func (ctx *Context) DomainCreateNew(config *DomainConfig) (Domid, error) { var cdomid C.uint32_t var cconfig C.libxl_domain_config err := config.toC(&cconfig) if err != nil { return Domid(0), fmt.Errorf("converting domain config to C: %v", err) } defer C.libxl_domain_config_dispose(&cconfig) ret := C.libxl_domain_create_new(ctx.ctx, &cconfig, &cdomid, nil, nil) if ret != 0 { return Domid(0), Error(ret) } return Domid(cdomid), nil } // DomainDestroy destroys a domain given a domid. func (ctx *Context) DomainDestroy(domid Domid) error { ret := C.libxl_domain_destroy(ctx.ctx, C.uint32_t(domid), nil) if ret != 0 { return Error(ret) } return nil } // SendTrigger sends a Trigger to the domain specified by domid. func (ctx *Context) SendTrigger(domid Domid, trigger Trigger, vcpuid int) error { ret := C.libxl_send_trigger(ctx.ctx, C.uint32_t(domid), C.libxl_trigger(trigger), C.uint32_t(vcpuid), nil) if ret != 0 { return Error(ret) } return nil }