/*
* 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
}